Cheat-sheets version 1.20

EN

Current version of Cheat-sheets contains a new user preference for changing the font size of the note area. This feature was several times asked and here it is! 😉

DE

Aktuelle Version der Spicker-App bekommt neue User-Einstellung zum Setzen der Schriftgröße im Note-Bereich. Diese Feature war mehrmals nachgefragt und da ist die! 😉

RU

В Шпаргалках появилась возможность менять размер шрифта в окне для ввода записи. Этой функций интересовались многие – и вот она … нарядная на праздник к нам пришла!

Cheat-sheets versions 1.18-1.19

EN

Minor bug fixes and … TEXT FILE IMPORT! The feature that “would make many people happy” as one guy wrote once in the app review. Happy cheating 😉

DE

Kleine Fehlerbehebungen und … IMPORT VON TEXTDATEIEN! Ein Feature was “viele Leute glücklich machen wird”, so lautet ein Kommentar zur Spicker-App. Frohes Spicken 😉

RU

Исправление незначительных ошибок и … ИМПОРТИРОВАНИЕ ТЕКСТОВЫХ ФАЙЛОВ, функция которая “сделает  многих людей счастливыми”, примерно так звучал один отзыв о Шпаргалках. Удачи на экзаменах! 😉

PolterApp Version 2.6

Mit dem aktuellen Update der PolterApp bekommen die Nutzer wieder die Möglichkeit Einträge aus der Sektionstabelle zu löschen, was unter iOS 12 plötzlich nicht mehr ging. Die PolterApp wurde auch im Kern etwas überarbeitet, bzw. optimiert, so dass die App nun weniger Speicher bei der Installation benötigt.

Objective-C: how to write to the iOS photo album and get the name of the written file

Using UIImagePickerController it is easy to take a picture in your iOS app. The picture is not immediately saved in the photo library. You must apply UIImageWriteToSavedPhotosAlbum method to write it there. In some situations you need to know the name of the file that the photo library assigns to the image. For example you have an option in your app also to select an image from the photo library. And you want to avoid that the user picks the same photo. So, how to know the name of the file that photo library assigns to the photo? There is a delegate method imagePickerController:didFinishPickingMediaWithInfo:. We make a use of it:

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    PHAsset *asset = nil;
    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
    fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
    if (fetchResult != nil && fetchResult.count > 0) {
        // we sorted the photos by creation date and get the last photo from Photos
        asset = [fetchResult lastObject];
    }
    
    if (asset) {
        // get photo info from this asset
        PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
        imageRequestOptions.synchronous = YES;
        [[PHImageManager defaultManager]
         requestImageDataForAsset:asset
         options:imageRequestOptions
         resultHandler:^(NSData *imageData, NSString *dataUTI,
                         UIImageOrientation orientation,
                         NSDictionary *info)
         {
             
             if ([info objectForKey:@"PHImageFileURLKey"]) {
                 // path looks like this -
                 // file:///var/mobile/Media/DCIM/###APPLE/IMG_####.JPG
                 NSURL *path = [info objectForKey:@"PHImageFileURLKey"];
                 if(path) {
                     NSString *filePath = [[LPPathUtilities applicationDocumentsFolderPath] stringByAppendingPathComponent:[path lastPathComponent]];
                     
                     NSData *dataFromImage = [NSData dataWithData:UIImageJPEGRepresentation(image, 10.0)];
                     if(![dataFromImage writeToFile:filePath atomically:YES]) {
                         // TODO: handle error
                     }
                 }
             }
         }];
    }
}

Unfortunately, the key PHImageFileURLKey is not specified in the Apple documentation. So, there is of course a danger, that this can key can change in the future. But I could not find so far a more easy approach how to get the file name. If you have a better idea, let me know 😉

WKWebView: no XSLT support

In our large iOS project that was started 2008 and that will be 10 years old this year we use much XSL transformations. This technology was the first choice at that time to deal with structured data and its representation on mobile devices. Nowadays similar uses cases can be covered by JSON and Java Script. Probably this is the reason, why WKWebView the newer component that Apple recommends to use instead of UIWebView contains no support for XSLT.

The only way to support XSLT in your app is to integrate some third party XSLT library. We decided for libxslt. That’s a C library based on libxml2. Instead of loading XML file in the WKWebView (this was possible with UIWebView) we start XSL transformation first and generate html file. Generated file is then loaded with WKWebView.

Objective C: Push Notifications in iOS 10

Apple introduced a new framework UserNotifications for delivering and handling of local and remote notifications. Let’s have a look at how to support PUSH notifications in an iOS app.

1. Import UserNotifications.framework in your AppDelegate file

#import <UserNotifications/UserNotifications.h>

Add UNUserNotificationCenterDelegate to the declaration:

#import <UserNotifications/UserNotifications.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>
 
@end

2. Register for PUSH notifications

The best place to register for PUSH notifications is the method application:didFinishLaunchingWithOptions:. According Apple’s docs deviceToken can change from time to time. That is why it is important not to cache the device token, but to request it every time on the app start.

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    [self registerForRemoteNotifications];
    return YES;
}

- (void)registerForRemoteNotifications
{
        // iOS 10 and greater
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        center.delegate = self;
        [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error){
            if(!error){
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[UIApplication sharedApplication] registerForRemoteNotifications];
                });
            }
        }];
}

In the code above you can see the call of requestAuthorizationWithOptions:completionHandler: method. It is important, otherwise no notifications will be displayed to the user. Quote from the Apple’s doc:

If you want your app’s remote notifications to display alerts, play sounds, or perform other user-facing actions, you must request authorization to do so using the requestAuthorizationWithOptions:completionHandler: method of UNUserNotificationCenter. If you do not request and receive authorization for your app’s interactions, the system delivers all remote notifications to your app silently.

It is also important to call registerForRemoteNotifications method on the main thread!

3. Handling of registration for remote notifications

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    // getting device token
    self.devToken = [self stringWithDeviceToken:deviceToken];
}


- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
   // TODO: handle errors here
    
}

// converts deviceToke to String
- (NSString *)stringWithDeviceToken:(NSData *)deviceToken {
    const char *data = [deviceToken bytes];
    NSMutableString *token = [NSMutableString string];
    
    for (NSUInteger i = 0; i < [deviceToken length]; i++) {
        [token appendFormat:@"%02.2hhX", data[i]];
    }
    
    return [token copy];
}

4. Handling delegate methods for UserNotifications

There are two more delegate methods to be implemented:

//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
    NSLog(@"User Info : %@",notification.request.content.userInfo);
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}
 
//Called to let your app know which action was selected by the user for a given notification.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler{
    NSLog(@"User Info : %@",response.notification.request.content.userInfo);
    completionHandler();
}

In iOS 10 it is different with remote notifications during the app is in the foreground. In former iOS versions we had to handle notifications ourselves and display notification to the user, e.g. as Alert message. Now notifications are always shown.

5. Add Push Notifications Entitlements if not done yet

That’s all.
Now your app can receive remote PUSH notifications. Happy Coding!

Elektronische Lieferschein-App ist kurz vorm Release

Seit März wird die neue elektronische Lieferschein-App bei Bentheimer Holz erprobt. Es sind erst mal zwei LKWs mit der App bestückt. Die Tester wurden recht früh in den Entwicklungsprozess eingebunden. Es ermöglicht nun im Testbetrieb nicht nur die Mängel zu entdecken, sondern wirklich die Richtung der App-Entwicklung zu beeinflussen. Aus dem wertvollen Feedback von LKW-Fahrern wird Optimierung am Interface und an Nutzbarkeit vorgenommen. Es entstehen Funktionen die sich aus dem täglichen Geschäft ergeben.

Es hat sich z. B. recht unpraktisch erwiesen, Polter und Lieferschein-Daten komplett händisch einzugeben. Als Maßnahme wurde QR-Code Scanner in die App integriert. QR-Code wird mit dem Fuhrauftrag ausgedrückt und kann aus der App heraus eingescannt werden. Die erforderlichen Daten-Felder werden automatisch vorausgefüllt.

Was noch offen ist, ist der Rücktransport der Daten. Die Lieferschein-App bietet die Möglichkeit Daten per Email als PDF oder als Text zu verschicken. Auch xml-Versandt ist möglich. Doch diese Schnittstellen sehen voraus, dass der Empfänger dafür zuständig ist, die Daten ins eigene Backend-System zu übernehmen. Da es fehleranfällig und lästig ist, wird aktuell an der automatisierten Synchronisation mit dem Backendsystem gearbeitet. Bei Bentheimer Holz ist es HIS, ein Holzinformationssystem, was auf MS Navision aufbaut.

 

 

PolterApp Versionen 2.4 – 2.5

Die letzten zwei Updates der PolterApp enthalten eine Reihe an Fehlerbehebungen. Insbesondere wurden die lästigen App-Abstürze beim schnellen Umschalten zwischen den Polter-Daten-Reitern behoben. Außerdem wurden alle Third-Party Komponenten, welche Bestandteile der PolterApp sind, auf den letzen Stand gebracht. Insbesondere Goolge-Maps.

Cheat-sheets version 1.16-1.17

EN

The last two updates of the Cheat-sheets 1.16 and 1.17 contain bug fixes reported by the users. Thanks for your feedback!

DE

Die letzten zwei Updates 1.16 und 1.17 der Spicker-App enthalten Fehlerbehebungen, die von Nutzern gemeldet waren. Vielen Dank für euer Feedback!

RU

Последние два апдейта Шпаргалок 1.16 и 1.17 содержать исправления ошибок, о которых сообщали пользователи. Спасибо за ваше участие!

Difference between merge and cherry-pick in git

Generally, cherry picking in git means to choose a particular commit from one branch and apply it onto another. In contrast  merge or rebase apply normally many commits onto another branch.

If you are a console fan and do not use any graphical interfaces working with git, proceed as follow for cherry-pick:

  1. Make sure you are on the branch you want to apply the commit to.

    git checkout master

  2. Execute the following to pick a commit:

    git cherry-pick <commit-hash>

Also note following:

  1. If you cherry-pick from a public branch, you should better use

    git cherry-pick -x <commit-hash>

    This will generate a standardized commit message. This way, you and your co-workers can still keep track of the origin of the commit.

  2. If you have notes attached to the commit they do not follow the cherry-pick. To bring them over as well, You have to use:

    git notes copy <from> <to>

 

More about cherry-pick can be found at git official guide page.