Posts Tagged ‘iOS’
Multiple iOS Simulator Support in Xcode 9
Xcode 9 was announced at WWDC17 earlier this year. I think it is probably one of the best Xcode releases to date, with a plethora of new features including a markdown editor which I am using for writing this post and considerably less buggy than it’s predecessors! One of the features that got me very excited was the ability to run multiple iOS simulators at the same time ! Finally …Now you can simulatneously test the app on different iOS versions and devices at the same time. The only limitation to the number of simulators you can run in parallel is the (memory/CPU) resources on your Mac. You would launch the app as usual by selecting the simulator as shown below If you want to run the app simulatneously on another simulator, you would go about that as shown below. That’s it!
There are many instances where you would need to support multiple configurations within your iOS app beyond the standard debug and release project configuration. Let’s suppose you app needs to talk to different server deployments – Development, QA and Production. In this case , you would want your app targeted for a specific environment to be configured to talk to the appropriate server deployment. This blog post describes how you can do that within your swift iOS app. Things are slightly different with Obj-C, where you could define preprocessor flags within your .xcconfig files created for each environment . But with limited support for macros in swift, we will follow a slightly different approach
Step 1: Create desired configurations for Dev, QA and Prod
- Under Project-Demo , add new configurations based upon existing Debug and Release configurations.
- Duplicate the Debug configuration as a “Dev” configuration and duplicate the Release configuration as “QA” and “Prod” configurations .
- Delete the Debug and Release configuration
Step 2: Add new schemes for Dev, QA and Prod configurations
Step 3: Make sure that the schemes created in Step 2 are “visible”
Step 4: Edit schemes created in Step2 and associate it with the appropriate configuration
So associate the QA scheme with QA configuration , the Dev scheme with Dev configuration and the Prod scheme with Prod configuration.
Step 5: Add a new user-defined build setting under Build Settings under tab.
This setting has a different value for each of the configurations.
Step 6: Add a info.plist property corresponding to the user-defined setting
Step 7: In your app, read the info.plist property
That's it. Take appropriate actions depending on the configuration setting.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. let deployName = Bundle.main.object(forInfoDictionaryKey: "Deployment") as! String switch deployName { case "DEV": print ("Dev Deployment. Connect to Dev server") case "QA": print ("QA Deployment. Connect to Dev server") case "PROD": print ("Prod Deployment. Connect to Dev server") default: print("Unsupported") } return true } |
A demo project can be downloaded from here.
Highcharts Support in a Native iOS App
There are many options available to display charts within your [native] iOS apps. You could write one yourself (why?) or use one of the many third party libraries that are available . Check out this GitHub repo for a list of charting libraries in swift/ ObjC.
However, if you are interested in using Highcharts within your app, then read on . Highcharts is a popular JS charting engine and you can learn more about it at http://www.highcharts.com.
This post will demonstrate how you can load a Highcharts based chart into a iOS native app. We will use the example charts available as part of the Highcharts library. A more interesting and complete example that demonstrates how you can dynamically load chart data is available at https://github.com/rajagp/iOS_Highcharts_Sample.
1) Download the sample xcode app project from http://www.priyaontech.com/wp-content/uploads/2017/01/HighchartDemo.zip. This is a simple single view application that will load a .html file into a WKWebView.
2) Download the Highcharts library from http://www.highcharts.com/download
3) Once downloaded , unzip its contents and examine it. You should see contents list similar to screenshot below
4) Navigate to the “examples” folder
5) Copy the “index.htm” file from any of the examples into your project. Make sure you copy it into your project
6) Your project should look something like this
7) In your xcode project, open and review the the index.htm file that you just copied over. It contains a JS function that creates a chart using the Highcharts chart API as defined in http://api.highcharts.com/highcharts/chart. The chart data and options are defined statically.
1 2 3 4 5 6 7 8 9 10 |
$(function() { // Create the chart Highcharts.chart('container', { chart: { type: 'column' }, title: { text: 'Browser market shares. January, 2015 to May, 2015' }, ……… |
8) Now, open the “ChartViewController.swift” file and review it. This is a simple view controller that manages a WKWebview and loads the index.html into the web view.
Look for the “loadContainerPage” function . This is where the index.htm is loaded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
fileprivate func loadContainerPage() { print(#function) // Loads the index.html that is the base page for loading the chart guard let filePath = Bundle(for: ChartViewController.self). path(forResource: "index", ofType: "htm") else{ print("Could not locate index.htm") return } do{ let currFrame = self.view.frame; let content = tryString(contentsOfFile:filePath) let formattedContent = String.init(format: content,currFrame.size.width,currFrame.size.height) let_= self.webView?.loadHTMLString(formattedContent, baseURL: URL.init(fileURLWithPath: Bundle(for: ChartViewController.self).bundlePath)) } catch{ print("Failed to load contents of index.html") } } |
9) That’s it ! Run the app and you should see the sample chart load …
A complete example that demonstrates how you can dynamically load chart data is available at https://github.com/rajagp/iOS_Highcharts_Sample.
MVC, MVP and MVVM Patterns on iOS
A software pattern is a template for solving a repeatable problem. Any user facing client app can be broadly composed of three main layers –
· The Presentation / UI Layer: Layer that a user interacts with.
· The Business Logic Layer: Layer that handles the data model and interactions for local/remote databases
· The Application Logic Layer : The glue between the Presentation and Business Logic Layers.
The Model View Controller (MVC):
The MVC is a well-known architectural pattern used for building iOS Apps.
· The View , which is generally not passive, corresponds to the Presentation Layer
· The Model , corresponds to the Business Logic Layer
· The Controller sits between the View and the Model , corresponds to the Application Logic Layer.
However, the controller becomes the catch-all for everything that isn’t exactly a UI control or a data model, leading to the “Massive View Controller” issue.
Two popular alternatives to the MVC pattern are the MVP and MVVM patterns, both of which are essentially variants of the MVC pattern.
The Model View Presenter (MVP):
The MVP defines the following
· The View , which is generally passive, corresponds to the Presentation Layer
· The Model , corresponds to the Business Logic Layer and is identical to the MVC pattern
· The Presenter sits between the View and the Model , corresponds to the Application Logic Layer.
A View is typically associated with one Presenter.
In iOS, the interaction between the View and Presenter can be implemented using the Delegation Pattern (https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html). The Delegation Pattern allows one object to delegate tasks to another object. In iOS, the pattern is implemented using a Protocol. The Protocol defines the interface that is to be implemented by the delegate.
In the MVP pattern, the View and Presenter are aware of each other. The Presenter holds a reference to the view it is associated with.
The PresenterProtocol defines the base set of methods that any Presenter must implement. Applications must extend this Protocol to include application specific methods.
1 2 3 4 |
protocol PresenterProtocol: class{ func attachPresentingView(_view:PresentingViewProtocol) func detachPresentingView(_view:PresentingViewProtocol) } |
The PresentingViewProtocol defines the base set of methods that View must implement. By providing default implementation of the methods in this interface, the conformant view does not have to provide its own implementation. This interface can be extended to define application specific methods.
1 2 3 4 5 6 |
protocol PresentingViewProtocol: class{ func dataStartedLoading() func dataFinishedLoading() func showErrorAlertWithTitle(_title:String?, message:String) func showSuccessAlertWithTitle(_title:String?, message:String) } |
The Model View View Model (MVVM) :
The MVVM defines the following
· The View , which is generally passive, corresponds to the Presentation Layer
· The Model , corresponds to the Business Logic Layer and is identical to the MVC pattern
· The View Model sits between the View and the Model , corresponds to the Application Logic Layer.
The key aspect of the MVVM pattern is the binding between the View and the View Model. In other words, the View is automatically notified of changes to the View Model.
In iOS, this can be accomplished using Key-Value-Observer (KVO) Pattern. In the KVO Pattern (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) , one object is automatically notified of changes to the state of another object. In Objective C, this facility is built into the run-time. However, it’s not as straightforward in Swift. One option would be to add the “dynamic” modifiers to properties that need to be dynamically dispatched. However, this is limiting as objects now need to be of type NSObject. The alternate is to simulate this behavior by defining a generic type that acts as a wrapper around properties that are observable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public struct DynamicType<T> { typealias ModelEventListener = (T)->Void typealias Listeners = [ModelEventListener] private var listeners:Listeners= [] var value:T? { didSet{ for(_,observer) inlisteners.enumerated() { if let value = value{ observer(value) } } } } mutating func bind(_listener:@escaping ModelEventListener) { listeners.append(listener) if let value = value{ listener(value) } } } |
The View can “bind” an appropriate callback to the ViewModel by using the “bind” method on the DynamicType. Every time the property value changes, the callback is automatically invoked.
Code and More:
A sample app that showcases the implementation of the MVC, MVP and MVVM pattern is available on GitHub at https://github.com/rajagp/iOS_MVC_MVP_MVVM_SampleApp
Slides from a related talk on supporting the MVP and MVVM patterns in iOS apps can be downloaded from iOS_MVP_MVVM_Patterns .
My Talk on Mobile Development At Girl Develop It
I had an awesome time presenting on Mobile Development at Girl Develop It, Ann Arbor group. There was an incredible level of interest in this topic that explained the good turnout despite the stormy weather.
The talk covered the basics of developing native apps for the iOS, Android and Windows platforms. In addition, an overview of some of the major cross-platform tools and frameworks including Appcelerator Titanium, Cordova PhoneGap and Xamarin Mono were provided. The talk also briefly addressed the concept of Responsive Web Design as it applies to mobile website and web apps.
You can download a copy of the talk from here and video of presentation is available on YouTube
Now a brief note on the evening itself : The topic of "Women in Technology" (or lack thereof) has always been an important to me and I am always looking for opportunities to help bridge the gender gap.
So it was encouraging to be speaking to an audience where women techies were in the majority (and a special shoutout to all the men who made it and showed their support to the group). Judging by the enthusiasm of the attendees, it is quite clear that women are just as curious and interested in technology as their male counterparts I believe that a forum that allows women to learn without fear of being judged and network comfortably with fellow women techies can be vital in encouraging more women to join the rest of us.
Supporting Dynamic Text in your iOS apps
I recently gave a talk on Dynamic Fonts at Cocoaheads meeting. You can download the presentation and sample code from here.
iOS7 introduced a cool feature called Dynamic Fonts. What it essentially allows you to do is to set your preferred reading text size in Settings (General or Accessibility) app of your phone and voilà, all apps that support dynamic type automatically adjust to display text content according to the preferred size setting
In this post, I’ll go over what you would need to do to support dynamic type within your apps so that yours will be one of the cool apps that reacts to the preferred text size change!
UITextKit, Text Styles and Font Descriptors
iOS7 introduced UITextKit – a powerful framework that allows you to support rich text content, layouts and without the complexities of drawing it with Core Text or having to use UIWebView.
An important component of UITextKit is TextStyles. Text Styles describe an intended use of a font .
The table below shows the list of supported text styles and the font descriptors that define them for the case when we set the preferred text size slider settings to the center.
Text Styles
|
Font Descriptor
|
UIFontTextStyleHeadline
(headings) |
NSCTFontUIUsageAttribute = UICTFontTextStyleHeadline;
NSFontNameAttribute = ".AppleSystemUIHeadline"; NSFontSizeAttribute = 17 |
UIFontTextStyleSubheadline (more…)
|
Functional Testing is system level , end-to-end testing of your app from a user's perspective. Automating the process of functional testing of your app offers several benefits – it saves time and effort, it's repeatable, simplifies regression testing, enables testing with large data sets and it can be tied into your Continuous Integration process. UI Automations is an automated functional test framework from Apple. Here, user interactions are driven by test scripts are written in JavaScript and executed using the Instruments Automation tool.
While there are several other automated functional test tools available including Calabash, KIF, Frank and Fone Monkey, UI Automations has the benefit that it is very simple to use, needs minimal or no changes to your existing app and since it's from Apple, it is (fairly) well supported and maintained.
I recently gave a talk on automated functional testing of iOS apps using UI Automations. You can download the presentation from this link. There is also a sample demo application with a corresponding set of UI Automation test scripts.
My Talk on “Intro to iOS Development” at ASEI
Today, I gave an introductory level technical talk about developing mobile apps for iOS platform at the American Society Of Engineers Of Indian Origin (ASEI), MI. The ASEI is "a two level (National and Local) non-profit organization of engineers and technical professionals of Indian origin". You can learn about them at http://www.aseimi.org. I was aware of the group but I had never attended any of their meetings, so I was not sure what to expect.
Right after work, I made the 45 mile drive to the ASEI meeting. Fighting the evening rush hour traffic, I reached there on the nick of time ; I probably made the organizers quite nervous!
There was a pretty good turn out. These were people, who just like me, had driven in from work and who probably had ten other places they'd rather be on a fall evening with picture perfect weather. I had to ensure that my talk was well worth their evening.
Soon after the featured mobile app presentation, I got started. I surveyed the room and learnt that there were less than five developers in the room. The rest of the audience was a mix of people with diverse backgrounds (different industries , different roles, varying demographics, a few were not even iPhone users).
My presentation was intended to be fairly technical , so my challenge was to make it appeal to the diverse audience. Although they were all not developers, I knew they all had one thing in common – they were very keen on learning more about iOS mobile development. I knew that was a start.
So for the next hour or so, I quickly moved through my slides. I had material for couple of hours but I tried to focus on material that would broadly appeal. Then the questions started pouring in and they were all very relevant. People were paying attention (well- at least most of them were) and it was interesting to see different perspectives.
I left the meeting with a greater sense of community.
You can download my presentation from here. It is intended to be a primer to the iOS platform and developing apps for it .
1 2 3 |
NSString *jsForTextSize = [[NSString alloc] initWithFormat:@"document.getElementsByTagName('body')[0].style. webkitTextSizeAdjust= '%d%%'", updatedFontSize*100/DEFAULTWEBVIEWFONTSIZE]; [self.myWebView stringByEvaluatingJavaScriptFromString:jsForTextSize]; |
In the JS above, DEFAULTWEBVIEWFONTSIZE refers to the default font size of the text content presented within the web view and updatedFontSize refers to the desired font size. So for example, if DEFAULTWEBVIEWFONTSIZE is 18 and updatedFontSize is 9, then updatedFontSize*100/DEFAULTWEBVIEWFONTSIZE evaluates to 50.
• Next step is to adjust the frame height of the web view so the scaled text content is visible. The simplest way to do this is to have the UIWebView as a subview of a scrollable view (UIScrollView or its subclass ). That way, you can adjust the web view frame height and correspondingly, the content size of the scroll view that encompasses it. Adjusting the web view height is a bit tricky as described below.
The sizeThatFits method on web view returns a size that best fits the content. The problem with this is that when you scale up, the method returns the updated size but if the web view is large enough to display the specified content, then scaling down will not update the frame size but instead, the current frame size is returned.
So first , reset the current frame height of the web view to a small value like 1.
1 2 3 |
CGRectadjustedFrame = self.myWebView.frame; adjustedFrame.size.height= 1; self.myWebView.frame= adjustedFrame; |
Now, obtain the frame size that would best fit the content. Since the current frame size is 1, we are scaling up. Update the frame size of the web view with the new frame size.
1 2 3 |
<span style="font-size: 14px;"><code> CGSizeframeSize = [self.myWebViewsizeThatFits:CGSizeZero]; adjustedFrame.size.height= frameSize.height; self.myWebView.frame= adjustedFrame;</code></span> |
Finally, update the content size of the UIScrollView containing the web view to accommodate the scaled content.
1 2 3 4 |
<span style="font-size: 14px;"> <code> CGSizescrollViewSize = self.myScrollView.contentSize; scrollViewSize.height= adjustedFrame.size.height+ self.myWebView.frame.origin.y; self.myScrollView.contentSize= scrollViewSize; </code> </span> |
You can download a sample project that scales the text content of web view from here. Shown below are some screenshots of the app.
Handling ToDo(s) and FixMe(s) in your code…
No comments
As an iOS developer (or any developer for that matter), it is not uncommon (or dare I say- its “very common”) for us to defer things while coding so as to not disrupt the current coding flow. For instance, handling of a special case or updating an algorithm with a more efficient one or just a reminder to double check a certain logic . To remind ourselves, we typically include a comment like “ToDo: Handle error code -1020” in our code.
Now, before we realize it, we have several “ToDos” and “FixMe” scattered throughout our project and unfortunately, many of them go unattended . Note that “attending” to them may be as simple as removing the comment because we have in fact handled the “To Do” but have just forgotten to remove the now obsolete comment which leads to clutter and confusing code.
So here is a neat little perl script that I found (and tweaked a wee bit for my purpose) that solves the problem. The script is executed every time my project is built. The purpose of script is simple. The script greps for keywords in all the .h and .m files in the ${SRCROOT} folder and prefixes it with a “warning” label.
Now, every time, I compile my project, the script runs and displays all statements that begin with keywords listed in the KEYWORDS reg expression show up as warnings. They are now hard to miss and I can ensure that all the “To Do”s are handled in a timely manner.
Then , cut and paste the script to the “Run Script” section
Now, when you build your app, you should see all comments that include the keywords mentioned in the script listed as warnings.