sábado, 23 de febrero de 2013

JAPanoView updated: Hotspots

Just updated my panorama viewer UIView subclass. It now supports hotspots: instantiate any UIView subclass and add it as a hotspot with the new method addHotspot:atHAngle:vAngle: indicating the horizontal angle (azimuth) and vertical angle (elevation). To remove a hotspot form a JAPanoView just call removeFromPanoView on the hotspot instance (like addSubview: and removeFromSuperview).

Since any UIView can be a hotspot, you can add any UIControl or add any UIGestureRecognizer to your hotspot and make it fully user interactive. You can also take advantage of the convertPoint/Rect:from/toView: UIView methods and show popovers from a hotspot as seen in the demo project:


[popover presentPopoverFromRect:hotspot.frame inView:hotspot.superview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];


There's also a new property for UIView shouldApplyPerspective. It indicates if the hotspot should be rendered always parallel to the screen (shouldApplyPerspective==NO) or perpendicular to the point-of-view/hotspot axis (shouldApplyPerspective==YES; default value).

You can get the code from the repository at bitbucket.

viernes, 22 de febrero de 2013

Integrating KSCrash with JIRA Mobile Connect

KSCrash is a powerful crash reporting library with a lot of extended features such as on-device symbolication. It offers an extensible plugin-like architecture to integrate with your favorite issue tracking platform, based on what KSCrash calls sinks. A sink is kind of a push endpoint for crash reports generated by KSCrash. In addition, KSCrash can be configured with no sink to automatically catch and store crash reports; KSCrash offers a set of APIs to query and retrieve stored crash reports.

JIRA Mobile Connect is a free, open-source library for collecting user feedback directly from your mobile app to JIRA issue tracking platform. It offers automated crash reporting using PLCrashReporter wich is, in my opinion, a bit outdated and falls short in features.

If you are an iOS developer using JIRA and want to ease and improve your automated crash reporting system, integrating JMC with KSCrash will save you lots of headaches and pain.

Push or pull

You can use KSCrash using push or pull crash report extraction. Using push, KSCrash is initialized using a sink that will be called when reports are available. Using pull, KSCrash is initialized with no sink, and some other code should query it to retrieve and process reports.

If you are not interested in any JMC feature apart from crash reporting, you can use push architecture writing a KSCrash sink that uses JMCCrashTransport class to send crashes to JIRA, but it's a bit tricky since JMCCrashTransport is not designed to be used directly and it has dependencies upon top level library classes such as JMC.

JMC uses a pull architecture for crash report extraction from PLCrashReporter. Thankfully, there's only one single point of integration between JMC and PLCrashReporter: the CrashReporter class. CrashReporter is a singleton that offers the following interface:

- (BOOL) hasPendingCrashReport;
- (NSArray*) crashReports;
- (void) cleanCrashReports;

Writing a new implementation of CrashReporter is easy, since KSCrash offers direct implementations of all three services. But KSCrash reports are returned in NSDictionary format and JMC expects you to return an array of NSStrings, so some processing should be taken to write the human readable report.

- (void)cleanCrashReports
{
    [[KSCrash instance] deleteAllReports];
}

-(BOOL)hasPendingCrashReport{
    return [[KSCrash instance] reportCount]>0;
}

In addition to sinks, KSCrash offers filters for report processing. Filters take an array of reports, process them and output them; usually yo configure a filter chain, where a filter takes input from another filter output, with a sink at the endpoint. But filters can be used on their own to do any sort of report processing, and KSCrash comes with a set of predefined filters. One of these filters transforms NSDictionary reports to NSString reports mimicking Apple's crash report format: KSCRashReportFilterAppleFmt.

- (NSArray *)crashReports
{
    KSCrashReportFilterAppleFmt *appleFormatFilter = [KSCrashReportFilterAppleFmt filterWithReportStyle:KSAppleReportStyleSymbolicated];
    __block NSArray *result=nil;
    
    /*
     WARNING: this code works since KSCrashReportFilterAppleFmt works synchronously. Using an asynchronous filter will result in undefined behaviour.
     To use async filters, make sure this method (crashReports) is not called on main thread and use GCD semaphores
     */
    [appleFormatFilter filterReports:[[KSCrash instanceallReportsonCompletion:^(NSArray *filteredReports, BOOL completed, NSError *error) {
        result=filteredReports;
    }];
    
    return result;
}

Note the warning. Filters take a completion block but there is no default behavior on how or when will the block be executed. The Apple format filter executes the block synchronously, but other filters/sinks may execute it asynchronously. To make it work with asynchronous filters you should use GCD semaphores and queues and make sure your code does not execute in the main thread since the semaphore could block it; you should never block the main thread, and even if you are thinking about it just forget: the filter/sink may use the main thread and you would be causing a deadlock.

ARC notes:
- KSCrash source files are ARC agnostic, it can be compiled using ARC or non-ARC
- JMC source files are non-ARC
- My CrashReport class code is ARC

So remember to set the appropriate -fno-objc-arc or -fobjc-arc flags as needed depending on your project global settings.

You can get the code for CrashReport class from a bitbucket repository.