Blog

  • Code for Two Essential Buttons for iOS: Write a Review and Send a Letter To Developers

    Code for Two Essential Buttons: Write a Review and Send a Letter To Developers. These buttons are often required in a mobile application to collect a feedback from users and to allow them write a review to your app more quickly and comfortable. It also includes some system information. It is in Russian, but no problem to translate it.

    - (void)writeReviewClicked
    {
      NSString *url = @"https://itunes.apple.com/ru/app/yourapp.ru/id662863746?mt=8";
    	NSString *escapedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    	[[UIApplication sharedApplication] openURL:[NSURL URLWithString:escapedUrl]];
    }
    
    - (void)sendLetterClicked
    {
      NSString *subject = [NSString stringWithFormat:@"%@ %@", @"Письмо от пользователя мобильного приложения", @"YourApp для iOS"];
    
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
      dateFormatter.timeStyle = NSDateFormatterMediumStyle;
      dateFormatter.dateStyle = NSDateFormatterMediumStyle;
      NSString *todayString = [dateFormatter stringFromDate:today];
    
      NSMutableString *temp = [NSMutableString new];
      [temp appendFormat:@"\n\n\n\n\n-----------------------\nCистемная информация может помочь нам в отладке приложения.\n-----------------------\n"];
      [temp appendFormat:@"Версия приложения: %@\n", UD_OBJECT(UDKeyVersion)];
      [temp appendFormat:@"Версия iOS: %@\n", [[UIDevice currentDevice] systemVersion]];
      [temp appendFormat:@"Устройство: %@\n", [[UIDevice currentDevice] deviceName]];
      [temp appendFormat:@"Интернет соединение: %@\n", [[RRHelper defaultHelper] networkStatusString]];
      [temp appendFormat:@"Имя пользователя: %@ \n", UD_OBJECT(UDKeyPersonEmail)];
      [temp appendFormat:@"Дата: %@ \n", todayString];
      [temp appendFormat:@"-----------------------\n"];
    
      MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
      [controller setSubject:subject];
      [controller setToRecipients:@[@"your@email.ru"]];
      [controller setMessageBody:temp isHTML:NO];
      controller.mailComposeDelegate = self;
      [self presentViewController:controller animated:YES completion:nil];
    }
    
    #pragma mark - MFMailComposeViewControllerDelegate
    
    - (void) mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    	switch (result) {
    		case MFMailComposeResultCancelled:
        {
    			break;
        }
    		case MFMailComposeResultFailed:
        {
          [RRHelper showErrorMessage:@"Не удалось отправить письмо."];
    			break;
        }
    		case MFMailComposeResultSent:
    			break;
    		default:
    			break;
    	}
    
      [self dismissModalViewControllerAnimated:YES];
      [self dismissViewControllerAnimated:YES completion:nil];
    }

    Use this category to get a more precise device name:

    //
    //  UIDevice+machine.m
    //
    //  Created by Dennis Kutlubaev on 15.11.13.
    //
    
    #import "UIDevice+machine.h"
    
    #import 
    
    @implementation UIDevice(deviceName)
    
    - (NSString*)deviceName {
    
      struct utsname systemInfo;
    
      uname(&systemInfo);
    
      NSString *machineName = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    
      NSDictionary *commonNamesDictionary =
      @{
        @"i386":     @"iPhone Simulator",
        @"x86_64":   @"iPad Simulator",
    
        @"iPhone1,1":    @"iPhone",
        @"iPhone1,2":    @"iPhone 3G",
        @"iPhone2,1":    @"iPhone 3GS",
        @"iPhone3,1":    @"iPhone 4",
        @"iPhone3,2":    @"iPhone 4(Rev A)",
        @"iPhone3,3":    @"iPhone 4(CDMA)",
        @"iPhone4,1":    @"iPhone 4S",
        @"iPhone5,1":    @"iPhone 5(GSM)",
        @"iPhone5,2":    @"iPhone 5(GSM+CDMA)",
        @"iPhone5,3":    @"iPhone 5c(GSM)",
        @"iPhone5,4":    @"iPhone 5c(GSM+CDMA)",
        @"iPhone6,1":    @"iPhone 5s(GSM)",
        @"iPhone6,2":    @"iPhone 5s(GSM+CDMA)",
    
        @"iPad1,1":  @"iPad",
        @"iPad2,1":  @"iPad 2(WiFi)",
        @"iPad2,2":  @"iPad 2(GSM)",
        @"iPad2,3":  @"iPad 2(CDMA)",
        @"iPad2,4":  @"iPad 2(WiFi Rev A)",
        @"iPad2,5":  @"iPad Mini(WiFi)",
        @"iPad2,6":  @"iPad Mini(GSM)",
        @"iPad2,7":  @"iPad Mini(GSM+CDMA)",
        @"iPad3,1":  @"iPad 3(WiFi)",
        @"iPad3,2":  @"iPad 3(GSM+CDMA)",
        @"iPad3,3":  @"iPad 3(GSM)",
        @"iPad3,4":  @"iPad 4(WiFi)",
        @"iPad3,5":  @"iPad 4(GSM)",
        @"iPad3,6":  @"iPad 4(GSM+CDMA)",
    
        @"iPod1,1":  @"iPod 1st Gen",
        @"iPod2,1":  @"iPod 2nd Gen",
        @"iPod3,1":  @"iPod 3rd Gen",
        @"iPod4,1":  @"iPod 4th Gen",
        @"iPod5,1":  @"iPod 5th Gen",
    
        };
    
      NSString *deviceName = commonNamesDictionary[machineName];
    
      if (deviceName == nil) {
        deviceName = machineName;
      }
    
      return deviceName;
    }
    
    @end

    And use this method in your helper class to create a user friendly network status string using Reachability class instance:

    - (NSString *)networkStatusString
    {
      NetworkStatus networkStatus = [AppDel.reachability currentReachabilityStatus];
      
      /*NotReachable = 0,
      ReachableViaWiFi = 2,
      ReachableViaWWAN = 1*/
      
      switch (networkStatus) {
        case NotReachable:
          _networkStatusString = @"Нет соединения.";
          break;
        case ReachableViaWiFi: {
          _networkStatusString = @"Wi-Fi";
          break;
        }
        case ReachableViaWWAN: {
          _networkStatusString = @"WWAN";
          break;
        }
        default:
          break;
      }
      
      return _networkStatusString;
    }
    

    References:
    http://stackoverflow.com/questions/8292246/how-to-programmatically-differentiate-between-iphone-4-and-iphone-4s
    http://iosdevelopertips.com/device/determine-if-iphone-is-3g-or-3gs-determine-if-ipod-is-first-or-second-generation.html

  • XCode 5 project doesn’t load – Solution

    Suddenly, after I closed Xcode with two projects opened, it stopped to launch. When I started Xcode it was trying to load projects and failed with error. I couldn’t open any project.

    After some search and some experiments I finally found a solution to this problem, that might look not very simple and lying on the surface. That is why I decided to post about it. May be this solution is not very good in case you don’t want to loose your unsaved code, but it works:

    Go to your libraries folder and delete the contents of Autosave Information folder.

    Here is how to do it:

    1. Open Finder

    2. Shift + Cmd  + G

    3.~/Library/Autosave Information, delete it

    4. It is also important to clean your trash bin!

    Then try to launch Xcode.

  • Instructions on how to create custom UITableViewCell with a xib

    It is a very fast and convenient way to create a custom UITableViewCell layout using Interface Builder. But Apple for some reason has not made this easy – you have to know some magic things to perform this task. There is a a ton of tutorials on this topic already. But nevertheless I decided to write my own one. So, these are step by step instructions:

    1. Create a UITableViewCell subclass, let’s name it RRMessageCell. When you create it, you will not get a xib by default, because XCode doesn’t create it.
    2. Add a xib file called RRMessageCell.
    3. In your xib file delete a view by default and add UITableViewCell object.
    4. Set a class for that new cell in xib and for File’s owner RRMessageCell in a right panel, 3rd tab of the Interface Builder.
    5. Add elements to your cell in xib: labels, imageViews, etc.
    6. Create outlets in your RRMessageCell class.
    7. Here is a first magic: do not bind outlets via files’ owner, but bind them via your RRMessageCell. It should look like this when you right click of your file’s owner:

    And it should look like this when you right click of a cell view:

    8. Now second magic: select your cell view in a xib file and in a 4th tab of right panel (attributes) set Identifier to ‘RRMessageCell‘. Here is illustration:

    9. Now you can close a xib file, open implementation file and write some code to set contents of a cell, using a custom setter for object, that is displayed in your cell class RRMessageCell.m:

    - (void)setMessage:(RRMessage *)message
    {
      _message = message;
      
      self.topicLabel.text = message.messageTheme;
      self.bodyLabel.text = message.messageText;
      
      NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
      [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
      [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
      NSString *dateString = [dateFormatter stringFromDate:message.messageDate];
      self.dateLabel.text = [NSString stringWithFormat:@"Отправлено: %@", dateString];
      
      self.arrowImageView.image = ([message.messageFromEmployer intValue] == 0) ? [UIImage imageNamed:@"arrow_from"] : [UIImage imageNamed:@"arrow_to"];
    }
    

    10. And now third magic – you have to create a cell this way in your UITableViewController:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
      
      static NSString *CellIdentifier = @"RRMessageCell";
      
      RRMessageCell *cell = (RRMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
      
      if ( cell == nil ) {
        
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"RRMessageCell" owner:self options:nil];
        cell = topLevelObjects[0];
        
      }
      
      RRMessage *message = _messages[indexPath.row];
      
      [cell setMessage:message];
      
      return cell;
    }
    

    P.S. Small tip: do not use “textLabel“, “detailTextLabel” variables in your custom UITableViewCell classes, because they will interrupt with native textLabel and you will get SIGABRT bug in runtime.

    P.S.S. If frames are drawn incorrectly, try to override layoutSubviews method of your custom class or remove autoresizing masks. This is another topic.

  • iOS AutoLayout Notes

    To transition the application to support iOS 7, Apple recommends to use Auto Layout extensively in their iOS 7 Transition Guide.

    Also at the recent time after about one year of creating intefaces completely programmatically, I again returned to using xibs to create TableViewCell layouts and other layouts rapidly. And now I definetely need to learn AutoLayout.

    So I decided to learn the features of AutoLayout from Auto Layout Guide and here I’m going to post some notes, that I took for myself to remember what to do and remember features of AutoLayout.

    Auto Layout is a system that lets you lay out your app’s user interface by creating a mathematical description of the relationships between the elements.

    The fundamental building block in Auto Layout is the constraint. Constraints express rules for the layout of elements in your interface; for example, you can create a constraint that specifies an element’s width, or its horizontal distance from another element.

    Constraints are cumulative, and do not override each other.

    Rather than writing an omniscient controller that calculates where views need to go for a given geometry, views become more self-organizing. This approach reduces the complexity of controller logic, and makes it easier to redesign views without requiring corresponding changes to the layout code.

    Leaf-level views such as buttons typically know more about what size they should be than does the code that is positioning them. This is communicated through the intrinsic content size, which tells the layout system that a view contains some content that it doesn’t natively understand, and indicates how large that content is, intrinsically.

    The easiest way to add, edit, or remove constraints is to use the visual layout tools in Interface Builder. Creating a constraint is as simple as Control-dragging between two views, or to add multiple constraints at once, you simply use the various pop-up windows.

    You can also add constraints using the Auto Layout menu, which resides on the Interface Builder canvas.

    Align. Create alignment constraints, such as centering a view in its container, or aligning the left edges of two views.

    Pin. Create spacing constraints, such as defining the height of a view, or specifying its horizontal distance from another view.

    Issues. Resolve layout issues by adding or resetting constraints based on suggestions.

    Resizing. Specify how resizing affects constraints.

    If you need to add a large set of constraints to describe your interface layout and you don’t want to add constraints one at a time, choose Issues > Add Missing Constraints to add a nonambiguous set of constraints.

    If you need to revert to a set of constraints without errors, or you just want to start over, choose Issues > Reset to Suggested Constraints to remove erroneous constraints and add a nonambiguous set of constraints.

    Here is a link on how to work with AutoLayout programmatically. I don’t need it yet, but it seems to be as easy, as using Interface Builder for this task.

  • Brief Info On What’s New in iOS 7 For Developers

    User Interface Changes

    • UI Redesign
    • – iOS 7 Design Resources

    • Dynamic Behaviors for Views
    • – apps can now specify dynamic behaviors for UIView objects and for other objects that conform to the UIDynamicItem protocol.

    • Text Kit
    • – Text programming guide

    64-Bit Support – when compiled for the 64-bit runtime, apps may run faster because of the availability of extra processor resources in 64-bit mode.

    Multitasking Enhancements – apps can register with the system and be launched periodically to retrieve that content in the background; apps that use push notifications to notify the user that new content is available can fetch the content in the background.

    Games

    • Sprite Kit Framework
    • – provides a hardware-accelerated animation system optimized for creating 2D and 2.5D games

    • Game Controller Framework
    • – lets you discover and configure Made-for-iPhone/iPod/iPad (MFi) game controller hardware in your app. Game controllers can be devices connected physically to an iOS device or connected wirelessly over Bluetooth

    • Game Center Improvements

    Maps – overlays, 3D map support, geodesic lines, map snapshots, direction related route information

    AirDrop – AirDrop support is now built in to the existing UIActivityViewController class.

    Inter-App Audio – enables the ability to send MIDI commands and stream audio between apps

    Peer-to-Peer Connectivity – supports the discovery of nearby devices and the direct communication with those devices

    New Frameworks

    • GameController.framework
    • SpriteKit.framework
    • MultipeerConnectivity.framework
    • JavaScriptCore.framework
    • MediaAccessibility.framework
    • SafariServices.framework
    • – provides support for programmatically adding URLs to the user’s Safari reading list

    Enhancements to Existing Frameworks

    • In the Message UI framework, the MFMessageComposeViewController class adds support for attaching files to messages.
    • The iAd framework introduces new methods on the MPMoviePlayerController class that let you run ads before a movie.
    • The framework extends the UIViewController class to make it easier to create ad-supported content. You can now configure your view controllers to display ads before displaying the actual content they manage.
    • The NSData class adds support for Base64 encoding.
    • The NSProgress class provides a general-purpose way to monitor the progress of an operation and report that progress to other parts of your app that want to use it.
    • The Core Telephony framework (CoreTelephony.framework) lets you get information about the type of radio technology in use by the device.
    • The Core Motion framework (CoreMotion.framework) adds support for step counting and motion tracking.
    • The Core Foundation framework (CoreFoundation.framework) now lets you schedule stream objects on dispatch queues.

    Objective-C – has been enhanced to support modules.

    Full description is in Apple iOS Developer Library.

  • Objective-C Fundamentals & Nuances

    You may write Objective-C code for 1-2 years, but still you may not know some fundamentals and nuances, that this programming language has. This may be a reason, why you fail to get a good job. Here I will post some important notes, that every iOS developer should understand though they may not be used often practically. This is taken from Cocoa fundamentals guide:

    1.
    Objective-C, the development language for Cocoa, is thoroughly object-oriented too, despite its grounding in ANSI C. It provides runtime support for message dispatch and specifies syntactical conventions for defining new classes. Objective-C supports most of the abstractions and mechanisms found in other object-oriented languages such as C++ and Java. These include inheritance, encapsulation, reusability, and polymorphism.

    2.
    But Objective-C is different from these other object-oriented languages, often in important ways. For example, Objective-C, unlike C++, doesn’t allow operator overloading, templates, or multiple inheritance.

    3.
    Every Objective-C object hides a data structure whose first member—or instance variable—is the isa pointer. (Most remaining members are defined by the object’s class and superclasses.) The isa pointer, as the name suggests, points to the object’s class, which is an object in its own right (see Figure 2-1) and is compiled from the class definition. The class object maintains a dispatch table consisting essentially of pointers to the methods it implements; it also holds a pointer to its superclass, which has its own dispatch table and superclass pointer. Through this chain of references, an object has access to the method implementations of its class and all its superclasses (as well as all inherited public and protected instance variables). The isa pointer is critical to the message-dispatch mechanism and to the dynamism of Cocoa objects.

    4.
    Objective-C is a very dynamic language. Its dynamism frees a program from compile-time and link-time constraints and shifts much of the responsibility for symbol resolution to runtime, when the user is in control. Objective-C is more dynamic than other programming languages because its dynamism springs from three sources:

    Dynamic typing—determining the class of an object at runtime

    Dynamic binding—determining the method to invoke at runtime

    Dynamic loading—adding new modules to a program at runtime

    5.
    For dynamic typing, Objective-C introduces the id data type, which can represent any Cocoa object. The id data type makes it possible to substitute any type of object at runtime. You can thereby let runtime factors dictate what kind of object is to be used in your code. Dynamic typing permits associations between objects to be determined at runtime rather than forcing them to be encoded in a static design. Static type checking at compile time may ensure stricter data integrity, but in exchange for that stricter integrity, dynamic typing gives your program much greater flexibility. And through object introspection (for example, asking a dynamically typed, anonymous object what its class is) you can still verify the type of an object at runtime and thus validate its suitability for a particular operation. (Of course, you can always statically check the types of objects when you need to.)

    6.
    Dynamic typing gives substance to dynamic binding, the second kind of dynamism in Objective-C. Just as dynamic typing defers the resolution of an object’s class membership until runtime, dynamic binding defers the decision of which method to invoke until runtime. Method invocations are not bound to code during compilation; they are bound only when a message is actually delivered. With both dynamic typing and dynamic binding, you can obtain different results in your code each time you execute it. Runtime factors determine which receiver is chosen and which method is invoked.

    The runtime’s message-dispatch machinery enables dynamic binding. When you send a message to a dynamically typed object, the runtime system uses the receiver’s isa pointer to locate the object’s class, and from there the method implementation to invoke. The method is dynamically bound to the message. And you don’t have to do anything special in your Objective-C code to reap the benefits of dynamic binding. It happens routinely and transparently every time you send a message, especially one to a dynamically typed object.

    7.
    Dynamic loading, the final type of dynamism, is a feature of Cocoa that depends on Objective-C for runtime support. With dynamic loading, a Cocoa program can load executable code and resources as they’re needed instead of having to load all program components at launch time. The executable code (which is linked prior to loading) often contains new classes that become integrated into the runtime image of the program. Both code and localized resources (including nib files) are packaged in bundles and are explicitly loaded with methods defined in Foundation’s NSBundle class.

    This “lazy-loading” of program code and resources improves overall performance by placing lower memory demands on the system. Even more importantly, dynamic loading makes applications extensible. You can devise a plug-in architecture for your application that allows you and other developers to customize it with additional modules that the application can dynamically load months or even years after the application is released. If the design is right, the classes in these modules will not clash with the classes already in place because each class encapsulates its implementation and has its own namespace.

  • “Open In…” in iOS apps

    I have added “Open in …” to my iOS app and answered a corresponding question in StackOverflow here.

  • Check if folder exists and create it, if it doesn’t in iOS

    It is interesting and quite uncommon that to check weather a folder exists, you should create a bool reference and pass it to the method fileExistsAtPath. This is a sample getter for a folderPath property, that I created. It can be used in various projects. It creates a folder in Documents folder of your app. Change amp to &.

    - (NSString *)folderPath
    {
        if (! _folderPath) {
            NSString *folderName = @"YourFolderName";
            NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *documentsDirectoryPath = [documentPaths objectAtIndex:0];
            _folderPath = [documentsDirectoryPath stringByAppendingPathComponent:folderName];
    
            // if folder doesn't exist, create it
            NSError *error = nil;
            NSFileManager *fileManager = [NSFileManager defaultManager];
            BOOL isDir;
            if (! [fileManager fileExistsAtPath:_folderPath isDirectory:&isDir]) {
                BOOL success = [fileManager createDirectoryAtPath:_folderPath withIntermediateDirectories:NO attributes:nil error:&error];
                if (!success || error) {
                    NSLog(@"Error: %@", [error localizedDescription]);
                }
                NSAssert(success, @"Failed to create folder at path:%@", _folderPath);
            }
        }
    
        return _folderPath;
    }

    Reference to StackOverflow

  • Using NSRegularExpression with NSAttributedString

    I think, it is a very convenient way to use regular expressions to find a range for applying attributes to NSAttributedString. This is how I did it:

        NSMutableAttributedString *goodText = [[NSMutableAttributedString alloc] initWithString:articleText];
    
        NSRange range = [articleText rangeOfString:@"\\[.+?\\]" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
        if (range.location != NSNotFound) {
            [goodText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Georgia" size:16] range:range];
            [goodText addAttribute:NSForegroundColorAttributeName value:[UIColor brownColor] range:range];
        }
    
        NSString *regEx = [NSString stringWithFormat:@"%@.+?\\s", [self.article.titleText substringToIndex:0]];
        range = [articleText rangeOfString:regEx options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
        if (range.location != NSNotFound) {
            [goodText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Georgia-Bold" size:20] range:range];
            [goodText addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:range];
        }
    
        [self.textView setAttributedText:goodText];

    I was searching for a list of available attributes and didn’t find them in a class reference’s first page. So I decided to post here information on that.

    Attributed strings support the following standard attributes for text.

        NSString *NSFontAttributeName;
        NSString *NSParagraphStyleAttributeName;
        NSString *NSForegroundColorAttributeName;
        NSString *NSUnderlineStyleAttributeName;
        NSString *NSSuperscriptAttributeName;
        NSString *NSBackgroundColorAttributeName;
        NSString *NSAttachmentAttributeName;
        NSString *NSLigatureAttributeName;
        NSString *NSBaselineOffsetAttributeName;
        NSString *NSKernAttributeName;
        NSString *NSLinkAttributeName;
        NSString *NSStrokeWidthAttributeName;
        NSString *NSStrokeColorAttributeName;
        NSString *NSUnderlineColorAttributeName;
        NSString *NSStrikethroughStyleAttributeName;
        NSString *NSStrikethroughColorAttributeName;
        NSString *NSShadowAttributeName;
        NSString *NSObliquenessAttributeName;
        NSString *NSExpansionAttributeName;
        NSString *NSCursorAttributeName;
        NSString *NSToolTipAttributeName;
        NSString *NSMarkedClauseSegmentAttributeName;
        NSString *NSWritingDirectionAttributeName;
        NSString *NSVerticalGlyphFormAttributeName;
        NSString *NSTextAlternativesAttributeName;

    Standart Attributes

    NSAttributedString programming guide

    A full class reference is here

     

  • Generating random BOOL and int values in Objective C

    I had to generate random BOOL value to create a random error condition in my app and to test, how this error is handled.

    So this is how I did it:

    srand(time(0));
    int randomval = rand() % 2;
    apiCall.shouldFailToLoad = (BOOL)randomval;
    NSLog(@"Should fail to load:%d", apiCall.shouldFailToLoad);
    

    We create a seed from current time moment for a rand function. Then we get a random number and a module from division by 2. It can be only 0 or 1. Then we tell our apiCall object to get a BOOL value, which tells it to fail sometimes.

    To create a random integer value use this:

    arc4random_uniform(upper_bound)
    

    It will return a uniformly distributed random number less than upper_bound. arc4random_uniform() is recommended over constructions like “arc4random() % upper_bound” as it avoids “modulo bias” when the upper bound is not a power of two.

    Reference: http://stackoverflow.com/questions/160890/generating-random-numbers-in-objective-c