Showing posts with label iOS. Show all posts
Showing posts with label iOS. Show all posts

Saturday, June 16, 2018

Uploading files to iCloud

This is the steps:

1) Enable iCloud service in the project.



2)

In the Info.plist, add the following settings. Please change the first "key" value (follow the same value as it was created in the Apple developer web portal) and also the folder name "My test folder".

<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.my.myTest180614</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>My test folder</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>

3) Download the open source library from the following URL. You need to add 4 files in iCloud folder (except the pch file).

https://github.com/iRareMedia/iCloudDocumentSync

4) Next thing is to follow the sample codes in the above open source home page.

Notes: whenever you made changes to the iCloud settings, make sure you increase the app build number. Otherwise, the changes might not reflect in iCloud service.

5) Upon app init, make sure that you run the following codes to init the iCloud service.

    [[iCloud sharedCloud] setupiCloudDocumentSyncWithUbiquityContainer:nil];
    [iCloud sharedCloud].verboseLogging = YES;
    
    BOOL cloudIsAvailable = [[iCloud sharedCloud] checkCloudAvailability];
    
    if (cloudIsAvailable) {
        [self showLog:@"cloud is available"];
    }
    else {
        [self showLog:@"cloud is NOT available"];
    }


6) To upload a file to iCloud:

    [[iCloud sharedCloud] uploadLocalDocumentToCloudWithName:@"my-test-file-local.txt" completion:^(NSError* err) {
        
        if (err == nil) {
            NSLog(@"error..");
        }
        
    }]; 

7) To check the file if it exist in the iCloud:

    NSURL* cloud_doc = [[f URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:@"Documents/my-test-file-local.txt"];

    BOOL b = [f isUbiquitousItemAtURL:cloud_doc];
    if (b) {
        [self showLog:@"=> my-test-file-local.txt- the file exist in the cloud"];
    }
    else {
        [self showLog:@"=> my-test-file-local.txt- the file does NOT exist in the cloud"];
    }

Notes: by default, the file stores in "Documents" folder!! Watch out for this behaviour.

8) To check if all files have been synced to local, make sure you handle the following delegate:

     iCloudDelegate

Handle this callback before start doing anything to the files (from the cloud):

     iCloudFileUpdateDidEnd


Saturday, March 17, 2018

HTML front end with back end with Objective-C


We are in the process of revamping all our iOS apps recently using: HTML+CSS+JavaScript to handle the front end and the back end is handled by Objective-C. The development became very interestingly for both UI designer and the programmer.

Before we took this approach, we were adding new functionality using Swift. After a few version upgrade on Swift/XCode, we found out that compilation error and warning will make us go mad. We don't think we can handle this and we don't like this to happen again and again. So, we decided to migrate all Swift codes that we have written back to Objective-C.

The next thing we are facing is the UI (user interface) and we are tiring in looking for solution on showing the popup and adjust the coordinate the popup appear in the correct position, etc. The storyboard file is particularly another bottleneck and wasted a lot of hours in "waiting for others to complete before they can start work".

With the new approach, the UI designer will work on the HTML+CSS. Once the layout has been confirmed by the team, the UI designer will continue to work on beautifying it and the Javascript & Objective-C programmer will start coding.

This approach reduce the development time as well as adding new features tremendously.

Time wasted in Swift version upgrade:
- New attribute
- Casting with ?, ?? or !....
- Casting between the data type: NSString, NSString?, NSString!....

Time wasted in implementing new idea using Storyboard:
- How to show a popup window.
- How to position the popup window correctly.
- How to partition a "view" into smaller view(s)/component(s) and then aggregate it to what we want.
- Screen flow/navigation.... try this API.. not this one, try another one... ah... obsolete API, look for new API... endless.. :(

With new approach:
 - HTML+CSS - that's my what my UI designer good at.
- Javscript - that's what we are doing for web application since 2008.
- Objective-C - this is not the area that we are good at and we can't afford to spend too much time in researching for the correct code. But, we need is to store the data and load the data. Since we are good in SQL statement and we decided to store the data in SQLite.

Future upgrade for the app...?
- Adding new HTML elements.. small problem.
- Reposition the popup... small problem.
- Revamp the color theme... small problem.. just replace the color and background color in CSS.
- Adding new screen... just add a new HTML file.
- Adding new function... just add a new Javascript to handle and some backend code in Objective C.

Our objective is to minimize the upgrade time and cost. Another thing is that we don't need to use any big framework that is already doing this (you can Google search for it) because those framework upgrade might break and we need to spends many hours in chasing their tail.

We love programming but we don't like to chasing the tail become someone decided to make some API obsolete and replace it by some new API.

:)

Sunday, March 11, 2018

ERROR ITMS-90717 while uploading app

While I was uploading my app to Apple App Store using Xcode 9, the following error appear:

ERROR ITMS-90717: "Invalid App Store Icon. The App Store Icon in the asset catalog in "xx.app" can't be transparent nor contain an alpha channel".

To resolve this error, you need to

1. Remove the alpha (i.e., the transparency attribute) in the image file. This can be done easily.

1.1. Double click on your app icon and the image will appear Preview app.
1.2. Choose File \ Export.
1.3. Untick "Alpha" (as shown below).



1.4. Click Save.

So, you have done the first step.

2. Regenerate all the icon file in various dimension.

3. Replace all the icon files in your project.

4. Last step, update the Images.xcassts file by dragging all appropriate
 icon file.

To check if all icon files have been updated, open the following directory in Finder. Ensure that all icon files' date/time matches the date/time that was done in step 2.

    [your app] \ Images.xcassets \  AppIcon.appiconset

5. Build and upload your app.

ERROR ITMS-90029 while uploading using Xcode 9

While I was uploading my app and encountered this error:

ERROR ITMS-90029: "Storyboard file 'Main~iphone.storyboardc' was not found. Please ensure that the specified file is included in the bundle with any required device modifiers appended to the filename".

What happened..? My app supports iPad only and how come it prompted an error on missing iPhone storyboard..?

I guess, there is some problem caused by upgrading the project. Why "Main~iphone.storyboardc"? I opened the .plist file and found this:

    Main storyboard file base name: "Main" <=== this value shown in the error message.
    Main storyboard file base name: "StoryboardV2" <== this is correct value.

Anyway, the problem can be rectify by

1. Opening the Target > your app. Look for "Devices" in the Deployment Info section. For my case, my value is "iPad".

2. Changing the "iPad" value to "Universal".

3. Set the Main Interface to any value.

4. Click on any source code file to force the new values to be saved to .plist.

5. Open the Devices in the Deployment Info section again.

6. Change the values back to "iPad" and the correct Main Interface value.

7. Archive and upload my app to App Store and the error has gone.







Sunday, July 30, 2017

Local or remote user notification

To use the local or remote user notification, first you need to add the reference to the UserNotifications framework.

   @import UserNotifications;

The most important is this code before using the user notification framework - you must get the user consent

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;

    [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound
                         completionHandler:^(BOOL granted, NSError* err) {
                             if (granted) {
                                 NSLog(@"granted notif");
                             }
                             
                         }];
    
    return YES;

}

For the "center.delegate = self" to work, you need to implement this protocol in the AppDelegate class:

    UNUserNotificationCenterDelegate

To schedule a local user notification, please use the following sample code which I found in the Internet:

- (IBAction)schedule_click:(id)sender {
    
    UNMutableNotificationContent *localNotification = [UNMutableNotificationContent new];
    localNotification.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:@"User1",@"Username", nil];
    localNotification.badge = [NSNumber numberWithInt:1];
    
    localNotification.sound = [UNNotificationSound defaultSound];
    
    // load the pre-defined mp3
    //localNotification.sound = [UNNotificationSound soundNamed:@"my.mp3"];
    
    localNotification.categoryIdentifier = @"myReminder";
    localNotification.body = @"Reminder to test";
    
    NSDate *date = [[NSDate date] dateByAddingTimeInterval:60];
    
    NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    
    /*NSCalendarUnitDay – Will set the repeatInterval to daily */
    NSDateComponents *dateComponents = [gregorian components:NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitTimeZone fromDate:date];
    
    UNCalendarNotificationTrigger *datetrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES];
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"Some Unique Value"
                                                                          content:localNotification
                                                                          trigger:datetrigger];
    
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        NSLog(@"added notif. Error= %@",error);
    }];

}


Say something... text to speech in iOS

In iOS, there is a text to speech framework which is able to say the text that you have typed.

The reference that you need in order to use the text to speech classes:

    @import AVFoundation;

Sample code:

    AVSpeechSynthesizer* speaker = [[AVSpeechSynthesizer alloc] init];
    AVSpeechUtterance* u = [[AVSpeechUtterance alloc] initWithString:@"Good morning. Boss."];

    u.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"];
    

    [speaker speakUtterance:u];


To get the list of the supported languages.

    NSArray<AVSpeechSynthesisVoice *> * v  = [AVSpeechSynthesisVoice speechVoices];
    
    AVSpeechSynthesisVoice* v2;
    for (int i = 0; i < v.count; i++) {
        v2 = [v objectAtIndex:i];
        NSLog(@"voice lang id=%@, code=%@", v2.identifier, v2.language);
    }

Please take note that there is duplicate entry for the same language code. This is because it has male and female voices and the identifier to the voice is different (i.e., unique). You need to call voiceWithIentifier method instead of voiceWithLanguage.

Tuesday, April 29, 2014

Creating multiple apps from one project

If you are planning to publishing multiple apps from one project (for example, light version and premium version), this will be the article that can help you out.

1. Click Manage Scheme.

2. Untick the "autocreate schemes" option. By unchecking this option, you can assign a proper scheme name.

3. Click on the project Targets. Two finger click on the "taget item" that you want to duplicate.

4. A new plist file will be added to the project. Rename the plist file to whatever you want. In our case, we use "premium version". Do the same for the "Bundle display name" and also "Bundle identifier".

  Note: you need two app ID. One for the light version and another for premium version.

5. Goto the project targets again. Rename the target to "premium". In Build Settings, search for "Product name" and update it as well.

6. Search "plist" in Build Settings and change it to the appropriate plist file name.

7. Click on the "scheme" and choose New Scheme.

8. Choose the "premium" from the target and then key in "premium" as the scheme name.

9. The final step will be setup the preprocessor macros. You need to edit both Debug and the Release.

  • For premium version, add "IS_PREMIUM_VERSION".
  • For light version, add "IS_LIGHT_VERSION".

10. In the coding, you have to do this:

 
     #ifdef IS_PREMIUM_VERSION
         lbl.text = @"this is premium Verison";
     #else
         lbl.text = @"this is Light version";
     #endif

Saturday, April 26, 2014

Find the user document directory in Simulator

One day, I want to backup my Sqlite database created inside the Simulator to my MacBook data directory and I was wondering if this is possible. Yes, it is possible. The files that was created in Simulator is physically located in your MacBook.

You can find the physical directory with the codes below:

  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *documentsDirectory = [paths objectAtIndex:0];

Sample output:

  /Users/{your Macbook user name}/Library/Application Support/iPhone Simulator/7.1/Applications/{random number}/Documents

Monday, September 2, 2013

Submitting your app to iTunes Connect

The following article contains the information on how to submit your app to iTunes Connect. The most important for the first time submission in the app statuses and also how to resolve the test case done by the QA.

https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/10_ManagingYourApplications/ManagingYourApplications.html


Saturday, August 17, 2013

Resetting XCode settings/preferences

In case of any problem with the IDE and you can't find any solution to solve it and you don't want to waste time in re-installing XCode, you may run the following commands to delete the existing settings/preferences. The following codes was posted by someone in stackoverflow.com and it solves the Storyboard problem after I change the minimum supported iOS version from 5.1 to 4.0.

First, you need to close XCode IDE and then call out the Terminal:

rm -rf $HOME/Library/Application Support/Developer/Shared/Xcode
rm -rf $HOME/Library/Preferences/com.apple.dt.Xcode.*
rm -rf $HOME/Library/Saved\ Application\ State/com.apple.dt.Xcode.savedState
rm -rf $HOME/Library/Developer/Xcode

NOTE: the first command is for Xcode 5 only.

Thursday, August 1, 2013

Changing the UITableView background color when it was UITableViewStyleGrouped

What you need to do is to run the following code in the viewDidLoad even:

    self.tableView.backgroundColor = [UIColor BlackColor];
    self.tableView.backgroundView = nil;

Saturday, June 22, 2013

Play system sound in iOS

Playing the system sound in iOS is quite easy.

  • Create a project and add AudioToolbox.framework reference. 
  • To play the system sound, call AudioServicesPlaySystemSound(1104) where "1104" is the system sound ID.
  • For the complete list of system sound, you may refers to http://iphonedevwiki.net/index.php/AudioServices
You might found the following sample code (somewhere in the Internet) which load the "Tock" sound from the system resources and plays it. But, the following code might not play the sound because you should not call "dispose" function immediately after "play" function as the play function will take some time to execute the "playing" process. This results in the soundID has been disposed/reset before it has been played. The correct way is to call the dispose function when you are unloading the view or app.

     NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.UIKit"] pathForResource:@"Tock" ofType:@"aiff"];
    
    SystemSoundID soundID;
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);
    AudioServicesPlaySystemSound(soundID);

    AudioServicesDisposeSystemSoundID(soundID);

Sunday, June 16, 2013

Shorthand to initialize NSDictionary

We use to initialize the NSDictionary variable like this:


        NSDictionary* f =[NSDictionary dictionaryWithObjectsAndKeys:@"a", @"CODE", nil];

Instead, you can write your code like this:
   
        NSDictionary* f =@{@"CODE": @"a"};

Note: don't forget the the key & value position in the above codes located differently.

Wednesday, June 12, 2013

Section in UITableViewController

In table view, you may create sections to group the cells. To do this, just follow the settings below:


UITableViewController settings

Declare a class that inherits from UITableViewCell and add a UILabel outlet. In CMyCell class, you may write some customized code. For example, when the view controller passing the data to the cell, the cell will display different controls based on the data.


Then, switch to the Storyboard, choose the"cell view" and set it's class name to "CMyCell".



Drag the label control to the "mainLabel" as shown below:



Sunday, June 2, 2013

Add the shared library into the workspace

As you are working on more and more project, you might wonder how to share the codes among these projects without have to copy the same class files. This can be done by creating a project with all your shared codes. Then, follow the steps below to add the shared project into the new project that you are working on.

Step 1: make sure that you have created a workspace in your project. This can be done by selecting Save As Workspace from File menu.

Step 2: right click on the project that you are working on and choose 'Add Files to "xxx"'. You will have to choose the "xxx.xcodeproj" file from the Finder.


Step 3: open up the project settings and goto Summary tab. Click on the "+" button as shown below.


Step 4: Choose your shared library from the Workspace.


Step 5: goto Buil Settings tab and search for "linker". Key in "-ObjC".


Step 6: You may continue to add new project references. Once you have done it, you might have to regroup the references into Frameworks. This step is optional.


Step 7: Finally, to use the classes that you have done in the shared project, you have to add the source code reference path. Goto Build Settings, search "search" text. Then, key in the shared project source code path in the "User Header Search Paths".


Tuesday, October 30, 2012

Compiling shared library project in workspace

When you a shared library project in a workspace and there are category functions, the compilation will fail. To overcome this problem, you need to add "-ObjC" in "Other Linker Flags" as shown below:



Monday, October 22, 2012

Showing drop down list

In iOS, there is not "combo box" or "drop down list" control as compare to Windows programming. But, there is a way to achieve this.

Below is the screen snapshot that tells you that yes, there is a way to show the drop down list when the user clicks on the "Choose.." button.



First, you have to add a table view controller to the Storyboard and set its' Identifier to "testTableView".


After that (in this new table view controller), you have to populate the data through the numberOfRowsInSection and cellForRowAtIndexPath method. Nothing special here.

In the didSelectRowAtIndexPath, you have to pass the user selection back to the "main view". "setSelection" is a method in the main view controller (the codes will be shown later).

- (void)tableView:(UITableView *)tableView
    didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString* s = [self.data objectAtIndex:indexPath.row];
    [self.main_view setSelection:s];
}

In the viewDidLoad, you have to write some code to control the popover size.

    CGRect rect = [self.tableView rectForSection:0];
    self.contentSizeForViewInPopover = CGSizeMake(200,
                                                  rect.size.height + 20);

In the .h class of the main view, you have to add the UIPopoverControllerDelegate protocol and also a popover controller variable.

   @interface CViewController : UIViewController<UIPopoverControllerDelegate>
   @property(strong)UIPopoverController* popover_controller;

In the .m file of the main view, you have to add two method for the above protocol.

   - (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController{
        return YES;
   }

   - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController{
      //do cleanup here, if any. 
   }

Through the StoryBoard, select the main view and you have to add the button reference and handle the TouchDown (like click event in the Windows programming). The codes are shown as below:

- (IBAction)choose_btn_onclick:(id)sender {
    if(self.popover_controller == nil)
    {
        CTestViewController* vc =[self.storyboard instantiateViewControllerWithIdentifier:@"testTableView"];
        vc.main_view = self;
       
        UIPopoverController* popover= [[UIPopoverController alloc] initWithContentViewController:vc];
        popover.delegate = self;  
        self.popover_controller = popover;
    }
   
    CGRect popover_rect = [self.view convertRect:[choose_btn frame]
                                        fromView:[choose_btn superview]];

    [self.popover_controller presentPopoverFromRect:popover_rect
                                             inView:self.view
                           permittedArrowDirections:UIPopoverArrowDirectionAny
                                           animated:YES];
}

The popover controller will pass the user selection through this method:

-(void)setSelection:(NSString*)s
{
    self.choose_btn.titleLabel.text = s;
    [self.popover_controller dismissPopoverAnimated:YES];
}

Tuesday, October 16, 2012

Updating UI by the background thread

As simple as this:

 // Add to top of file
 #import <dispatch/dispatch.h>
 
 if ([NSThread isMainThread])
 {
  [self MyMethodName];
 }
 else
 {
  dispatch_sync(dispatch_get_main_queue(), ^{
   //Update UI in UI thread here
   [self MyMethodName];
  });
 }
 
Reference:
http://www.ios-developer.net/iphone-ipad-programmer/development/threads/updating-ui-controls-on-background-threads
http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial

Tuesday, October 9, 2012

Showing alert on the screen

In .Net, we are using MessageBox to show the alert on the screen. In iOS, we are going to use UIAlertView.

Below is the sample code copy

        UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"Helo world"
            message:@"This is the popup message." 
            delegate:self cancelButtonTitle:@"OK"
            otherButtonTitles:@"Clear", nil]];
        [av show];