Dailycode.info

Short solution for short problems

iOS: Open second storyboard in modal with presentation curl.

I had a very large StoryBoard with a lot of relations. I started to group functionality and add the Views to different storyboards. Since iOS 7 and xcode 5 it came in a hurry, since the designer was crashing and messing up the layout.

First I just pushed the viewcontrollers in the navigation controller like this:

+(void) NavigateToCustomerLookupFrom:(UIViewController *)vc

{

    UIStoryboard * sb = [UIStoryboardstoryboardWithName:@"CustomerLookupStoryboard"bundle:nil];

    UIViewController * vcTo = [sb instantiateInitialViewController];

    

    [UIViewtransitionWithView:vc.viewduration:0.8options:UIViewAnimationOptionTransitionCrossDissolve

                    animations:^{

                        [vc.navigationController pushViewController:vcTo animated:NO];

                    }

                    completion:NULL];

    

}

That worked. But then I came up with a views that needed to be opened in a modal way. So this is the code to do just that. In this example I use the partial curl effect to open it:

+(void) NavigateToSignatureFrom:(UIViewController *)vc withSignee:(NSString *) Signee

{

    UIStoryboard * sb = [UIStoryboardstoryboardWithName:@"SignatureStoryboard"bundle:nil];

    UIViewController * vcTo = [sb instantiateInitialViewController];

    

    ((SignatureVC *) vcTo).Signee = Signee;

    

    [vcTo setModalTransitionStyle:UIModalTransitionStylePartialCurl];

    [vc presentViewController:vcTo animated:YEScompletion:nil];

}

The example also shows how to set properties of the destination controller.


xcode5: story board crashes

After upgrading to xcode 5, my xcode crashed when I opened a story board with some 20 Views on it. 

I tried several solutions, including waiting, clicking, wainting. But everytime the xcode seemed like loading and responded for a click every minute or so.

The reason the story board crashed, was apparently because I clicked once on the keep xcode 4.6 compatible. This made xcode crash. It started scanning the files and ...

So how to solve thisjQuery15201013488220050931_1381145930681

Right click on the storyboard and choose: 'Open As' -> Source code.

There you need to change the top of the document to be xcode 5 comatible. You can get the code from a story board in your solution that is working, or you can try to replace the code on the top of the document up until </dependencies> with this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4510" systemVersion="12F37" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES">

    <dependencies>

        <deployment defaultVersion="1552" identifier="iOS"/>

        <development version="5000" identifier="xcode"/>

        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3742"/>

    </dependencies>

 

You can now switch the View As to 'iOS 6.1 and Earlier' or 'iOS 7.0 and Later'.

You'll find this in the identity inspector when clicking on the story board outside of a view.


xcode5: story board crashes

After upgrading to xcode 5, my xcode crashed when I opened a story board with some 20 Views on it. 

I tried several solutions, including waiting, clicking, wainting. But everytime the xcode seemed like loading and responded for a click every minute or so.

The reason the story board crashed, was apparently because I clicked once on the keep xcode 4.6 compatible. This made xcode crash. It started scanning the files and ...

So how to solve thisjQuery15201013488220050931_1381145930680

Right click on the storyboard and choose: 'Open As' -> Source code.

There you need to change the top of the document to be xcode 5 comatible. You can get the code from a story board in your solution that is working, or you can try to replace the code on the top of the document up until </dependencies> with this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4510" systemVersion="12F37" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES">

    <dependencies>

        <deployment defaultVersion="1552" identifier="iOS"/>

        <development version="5000" identifier="xcode"/>

        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3742"/>

    </dependencies>

 


iOS: UITable view (Server Side) Lazy Loading

I'm creating an app that will be used in factories. At the test factories, the application runs fast and smooth. But when starting to deploy to other factories where the workflow is different, I had some grids with loading times of 5 to 7 seconds. They didn't liked it and so did I. So then I had to build in paging in the grids. Since I'm working with a .Net backend server, I will post this answer using LINQ on the server and iOS on the app.

The solution is to show the first 50 elements in the grid, when the 50st element is shown, he has to get the next 50 element and so on... The implementation is actually very simple. I pass the itemCount and the pageSize to the server. So you will need to add these arguments to the getter of your data. On server side I just add a simple Skip and Take using link. Skip the first ... items and Take the (pageCount) number of items. Thats it. So it took me 30 minutes to implement it for the first grid, the next will be 5 minutes of work. 

First on the app side, add this variable

int iPageSize=0;

Then in the CellForRowAtIndexPath method you''l need to check the number of items and load the following items before you return the cell. The store.PageItemCount is a global variable that defines the max items per page, I made it global so it can be used on all grids and variable so we can change the item size on the device.

int indexRow = indexPath.row+1;

int iPageSizeSum = iPageSize+store.PageItemCount;

if (indexRow >= iPageSizeSum)

{

    iPageSize = iPageSize + store.PageItemCount;

    [self configureView];

}

 

in the configureView I add the items to a mutable array and reload the table:

    

[self.ordersOnJig addObjectsFromArray:(NSMutableArray *) value];

[self.locationsTable reloadData];

[self.locationsTable reloadInputViews];

Now the server side integration is even more simple:

returnList = returnList.Skip(skip).Take(pageSize).ToList();

return returnList;



iOS : SUDZC cannot serialize date from .Net WCF

SUDZC is working fine for me. (That rhymes :-) Strangely enough all dates are deserialized correct on the IPAD. All dates coming from the database that is. But when filled a date directly from the .Net web service, it was not deserializing. The reason is that the date format was different. WCF serializes the date into a larger format.

Dates coming from the database in the entities were in this format:

 

2013-01-04T13:19:55.253

 

No problem there. But then when I filled a date into the same entity directly in the web service code, the date looked like this after serialization:

 

2013-03-20T15:31:20.560557+01:00

 

So I improved the deserialization method for dates a little for SUDZC to handle these dates. You can find the method in the SOAP.m class (Do not forget to remove the NSLogs after you're done):

// Converts a string to a date.

+ (NSDate*) dateFromString: (NSString*) value

{

NSLog(@"val: %@",value);

    if([value rangeOfString:@"T"].length != 1) {

value = [NSString stringWithFormat:@"%@T00:00:00.000", value];

}

if([value rangeOfString:@"."].length != 1) {

value = [NSString stringWithFormat:@"%@.000", value];

}

    if (value.length > 23)

        value = [value substringToIndex:23];

    

if(value == nil || [value isEqualToString:@""]) { return nil; }

NSDate* outputDate = [[Soap dateFormatter] dateFromString: value];

    NSLog(@"output: %@",outputDate);

return outputDate;

}

 


iOS: Format UITextView as UITextField

It's really simple and strange that it's not made by default.

I found a good solution that works perfect and you can see no difference between TextField and TextView, except for the height.

I made a small function that will style your TextView like a TextField:

+(void) MakeTextField:(UITextView *) vw

{

    vw.backgroundColor = [UIColorclearColor];

    UIImageView *borderView = [[UIImageViewalloc] initWithFrame: CGRectMake(0, 0, vw.frame.size.width, vw.frame.size.height)];

    borderView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;

    UIImage *textFieldImage = [[UIImageimageNamed:@"TextField.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 8, 15, 8)];

    borderView.image = textFieldImage;

    [vw addSubview: borderView];

    [vw sendSubviewToBack: borderView];

}

Just call this function on the DidLoad and pass the TextViews you want to format:

- (void)viewDidLoad

{

    [superviewDidLoad];

// Do any additional setup after loading the view.

    [GeneralZPFunctionsMakeTextField:self.txtInformation];

}

You will also need this picture and call it TextField.png:

Thats it! Below you see 2 examples of UITextView that look like a UITextField!

Source: http://stackoverflow.com/questions/1824463/how-to-style-uitextview-to-like-rounded-rect-text-field


iOS : Save file to windows file share.

I had tried several solutions. The first that really worked stable was to use http post to send the data to a php recieving file. The solution can be found here. But that was hard to control and it fails when files get bigger then 2Mb. It was also relative slow. 

So my current solution works fantastic!!! I'm using FTP now to send files from my iOS device to the Windows server. The only thing you need to do on server side is to enable ftp and create and ftp site. I also used basic authentication with an active directory user. 

On iOS site I followed this example from apple themselves and create a ViewController that can handle all your uploading. Based on file path or NData and file name. You can simply call the class like this:

 

self.uploadController.parentView = self.view;

self.uploadController.dataToSend = imageData;

self.uploadController.fileName = @"MyFile.PNG";

[self.uploadController UploadPressed:sender];

 

First we tell which parent view to use. This is to show the progress of the file upload.

Then we provide the NSData which can be read from image or video or any other kind of resource. Here you get an example of a picture that is on an image view:

 

NSData *imageData = UIImagePNGRepresentation(self.imageView.image);

self.uploadController.dataToSend = imageData;

 

And here an example of a video based on the video's url on the device:

 

NSData *videoData;

if(self.videoUrl)

{

      videoData = [NSData dataWithContentsOfURL:self.videoUrl];

}

        

if (videoData)

{

      self.uploadController.dataToSend = videoData;

      ...

 

So thats really it. It will send your files to the url which you had defined in your App using the user and password. Now here's the ViewController that does all the work for you:

The UploadVC.h

//

//  UploadV3

//  TestBARCodeAndSIngnature

//

//  Created by Mark Deraeve on 28/01/13.

//  Copyright (c) 2013. All rights reserved.

//

 

#import <UIKit/UIKit.h>

#include <CFNetwork/CFNetwork.h>

 

@interface UploadVC : UIViewController <NSStreamDelegate>

 

@property (nonatomicassignreadonly ) BOOL              isSending;

@property (nonatomicstrongreadwriteNSOutputStream *  networkStream;

@property (nonatomicstrongreadwriteNSInputStream *   fileStream;

@property (nonatomicassignreadonly ) uint8_t *         buffer;

@property (nonatomicassignreadwritesize_t            bufferOffset;

@property (nonatomicassignreadwritesize_t            bufferLimit;

@property (nonatomicstrongNSData * dataToSend;

@property (nonatomicstrongNSString * fileName;

@property (nonatomicstrongNSString * filePath;

@property (nonatomicstrongUIView * parentView;

 

- (void)UploadPressed:(id)sender;

@end

The UploadVC.m

//

//  UploadV3

//  TestBARCodeAndSIngnature

//

//  Created by Mark Deraeve on 28/01/13.

//  Copyright (c) 2013. All rights reserved.

//

 

#import "UploadVC.h"

#import "NetworkManager.h"

#import "VariableStore.h"

#import "GeneralMESFunctions.h"

 

 

enum {

    kSendBufferSize = 32768

};

 

@interfaceUploadVC ()

 

 

 

@end

 

@implementation UploadVC

{

    uint8_t                     _buffer[kSendBufferSize];

}

 

VariableStore * store;

MBProgressHUD * hud;

 

- (void)sendDidStart

{

    [[NetworkManagersharedInstancedidStartNetworkOperation];

}

 

- (void)updateStatus:(NSString *)statusString

{

    assert(statusString != nil);

}

 

- (void)sendDidStopWithStatus:(NSString *)statusString

{

    if (statusString == nil) {

        statusString = @"Put succeeded";

    }

    [[NetworkManagersharedInstancedidStopNetworkOperation];

}

 

#pragma mark * Core transfer code

 

// This is the code that actually does the networking.

 

// Because buffer is declared as an array, you have to use a custom getter.

// A synthesised getter doesn't compile.

 

- (uint8_t *)buffer

{

    returnself->_buffer;

}

 

- (BOOL)isSending

{

    return (self.networkStream != nil);

}

 

- (void)startSend:(NSString *) filePath

{

    BOOL                    success;

    NSURL *                 url;

    hud = [GeneralMESFunctionsGetMBProgressHUDForView:self.parentView];

    hud.detailsLabelText = @"Uploading file...";

 

    assert(filePath != nil);

    assert([[NSFileManagerdefaultManagerfileExistsAtPath:filePath]);

    assert( [filePath.pathExtensionisEqual:@"png"] || [filePath.pathExtensionisEqual:@"jpg"] );

    

    assert(self.networkStream == nil);      // don't tap send twice in a row!

    assert(self.fileStream == nil);         // ditto

    

    // First get and check the URL.

    

    url = [[NetworkManagersharedInstancesmartURLForString:store.MESFTPPath];

    success = (url != nil);

    

    if (success) {

        // Add the last part of the file name to the end of the URL to form the final

        // URL that we're going to put to.

        

        url = CFBridgingRelease(

                                CFURLCreateCopyAppendingPathComponent(NULL, (__bridge CFURLRef) url, (__bridge CFStringRef) [filePath lastPathComponent], false)

                                );

        success = (url != nil);

    }

    

    // If the URL is bogus, let the user know.  Otherwise kick off the connection.

    

    if ( ! success) {

        //self.txtStatus.text = @"Invalid URL";

    } else {

        

        // Open a stream for the file we're going to send.  We do not open this stream;

        // NSURLConnection will do it for us.

        

        self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];

        assert(self.fileStream != nil);

        

        [self.fileStream open];

        

        // Open a CFFTPStream for the URL.

        

        self.networkStream = CFBridgingRelease(

                                               CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) url)

                                               );

        assert(self.networkStream != nil);

        

        if ([store.MESFTPUser length] != 0) {

            success = [self.networkStream setProperty:store.MESFTPUser forKey:(id)kCFStreamPropertyFTPUserName];

            assert(success);

            success = [self.networkStream setProperty:store.MESFTPPWD forKey:(id)kCFStreamPropertyFTPPassword];

            assert(success);

        }

        

        self.networkStream.delegate = self;

        [self.networkStreamscheduleInRunLoop:[NSRunLoopcurrentRunLoopforMode:NSDefaultRunLoopMode];

        [self.networkStream open];

        

        // Tell the UI we're sending.

        

        [self sendDidStart];

    }

}

 

- (void)startSend:(NSData *) dataToSend fileName:(NSString *) fileName

{

    BOOL                    success;

    NSURL *                 url;

    

    hud = [GeneralMESFunctionsGetMBProgressHUDForView:self.parentView];

    hud.detailsLabelText = @"Uploading file...";

    

    assert(dataToSend != nil);

    assert(self.networkStream == nil);      // don't tap send twice in a row!

    assert(self.fileStream == nil);         // ditto

    

    // First get and check the URL.

    

    url = [[NetworkManagersharedInstancesmartURLForString:store.MESFTPPath];

    success = (url != nil);

    

    if (success) {

        // Add the last part of the file name to the end of the URL to form the final

        // URL that we're going to put to.

        

        url = CFBridgingRelease(

                                CFURLCreateCopyAppendingPathComponent(NULL, (__bridge CFURLRef) url, (__bridge CFStringRef) fileName, false)

                                );

        success = (url != nil);

    }

    

    // If the URL is bogus, let the user know.  Otherwise kick off the connection.

    

    if ( ! success) {

        //self.txtStatus.text = @"Invalid URL";

    } else {

        

        // Open a stream for the file we're going to send.  We do not open this stream;

        // NSURLConnection will do it for us.

        

        self.fileStream = [NSInputStream inputStreamWithData:dataToSend];

        assert(self.fileStream != nil);

        

        [self.fileStream open];

        

        // Open a CFFTPStream for the URL.

        

        self.networkStream = CFBridgingRelease(

                                               CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) url)

                                               );

        assert(self.networkStream != nil);

        

        if ([store.MESFTPUser length] != 0) {

            success = [self.networkStream setProperty:store.MESFTPUser forKey:(id)kCFStreamPropertyFTPUserName];

            assert(success);

            success = [self.networkStream setProperty:store.MESFTPPWD forKey:(id)kCFStreamPropertyFTPPassword];

            assert(success);

        }

        

        self.networkStream.delegate = self;

        [self.networkStreamscheduleInRunLoop:[NSRunLoopcurrentRunLoopforMode:NSDefaultRunLoopMode];

        [self.networkStream open];

        

        // Tell the UI we're sending.

        

        [self sendDidStart];

    }

}

 

- (void)stopSendWithStatus:(NSString *)statusString

{

    if (self.networkStream != nil) {

        [self.networkStreamremoveFromRunLoop:[NSRunLoopcurrentRunLoopforMode:NSDefaultRunLoopMode];

        self.networkStream.delegate = nil;

        [self.networkStream close];

        self.networkStream = nil;

    }

    if (self.fileStream != nil) {

        [self.fileStream close];

        self.fileStream = nil;

    }

    [MBProgressHUDhideHUDForView:self.parentViewanimated:YES];

    [self sendDidStopWithStatus:statusString];

}

 

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode

// An NSStream delegate callback that's called when events happen on our

// network stream.

{

#pragma unused(aStream)

    assert(aStream == self.networkStream);

    

    switch (eventCode) {

        caseNSStreamEventOpenCompleted: {

            [selfupdateStatus:@"Opened connection"];

        } break;

        caseNSStreamEventHasBytesAvailable: {

            assert(NO);     // should never happen for the output stream

        } break;

        caseNSStreamEventHasSpaceAvailable: {

            [self updateStatus:@"Sending"];

            

            // If we don't have any data buffered, go read the next chunk of data.

            

            if (self.bufferOffset == self.bufferLimit) {

                NSInteger   bytesRead;

                

                bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize];

                

                if (bytesRead == -1) {

                    [self stopSendWithStatus:@"File read error"];

                } else if (bytesRead == 0) {

                    [self stopSendWithStatus:nil];

                } else {

                    self.bufferOffset = 0;

                    self.bufferLimit  = bytesRead;

                }

            }

            

            // If we're not out of data completely, send the next chunk.

            

            if (self.bufferOffset != self.bufferLimit) {

                NSInteger   bytesWritten;

                bytesWritten = [self.networkStreamwrite:&self.buffer[self.bufferOffsetmaxLength:self.bufferLimit - self.bufferOffset];

                assert(bytesWritten != 0);

                if (bytesWritten == -1) {

                    [self stopSendWithStatus:@"Network write error"];

                } else {

                    self.bufferOffset += bytesWritten;

                }

            }

        } break;

        caseNSStreamEventErrorOccurred: {

            [selfstopSendWithStatus:@"Stream open error"];

        } break;

        caseNSStreamEventEndEncountered: {

            // ignore

        } break;

        default: {

            assert(NO);

        } break;

    }

}

 

- (void)viewDidLoad

{

    [superviewDidLoad];

// Do any additional setup after loading the view, typically from a

}

 

- (void)didReceiveMemoryWarning

{

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

- (void)UploadPressed:(id)sender;

{

    store = [VariableStoresharedInstance];

    if (self.dataToSend && self.fileName)

    {

        [selfstartSend:self.dataToSendfileName:self.fileName];

    }

    else

    {

        [self startSend:self.filePath];

    }

}

 

@end

 

As you can see, you're missing some files. I use a simple progress window MBProgressHUD, for better user experience, but you can loose this. You could also use the activity view. Then I get my settings from a class called Variable store. This is the place were I put all my shared variables used throughout the program. So there I get the url, user and password for the file share. This you can also put hardcoded or get is from your own singleton class. First test are very good. Tested with large video files and pictures.  The last file you will need is the NetWorkManager written by apple:

NetworkManager.h:

 

/*

 File:       NetworkManager.h

 

 Contains:   Shared state and utilities for networking.

 

 Written by: DTS

 

 Copyright:  Copyright (c) 2009-2012 Apple Inc. All Rights Reserved.

 

 */

 

#import <Foundation/Foundation.h>

 

@interface NetworkManager : NSObject

 

+ (NetworkManager *)sharedInstance;

 

- (NSURL *)smartURLForString:(NSString *)str;

- (BOOL)isImageURL:(NSURL *)url;

 

@property (nonatomicassignreadonly ) NSUInteger     networkOperationCount;  // observable

 

- (void)didStartNetworkOperation;

- (void)didStopNetworkOperation;

 

@end

 
NetworkManager.m
 

/*

 File:       NetworkManager.m

 

 Contains:   Shared state and utilities for networking.

 

 Written by: DTS

 

 Copyright:  Copyright (c) 2009-2012 Apple Inc. All Rights Reserved.

 

 */

 

#import "NetworkManager.h"

 

@interface NetworkManager ()

 

// read/write redeclaration of public read-only property

 

@property (nonatomicassignreadwriteNSUInteger     networkOperationCount;

 

@end

 

@implementation NetworkManager

 

@synthesize networkOperationCount = _networkOperationCount;

 

+ (NetworkManager *)sharedInstance

{

    static dispatch_once_t  onceToken;

    static NetworkManager * sSharedInstance;

    

    dispatch_once(&onceToken, ^{

        sSharedInstance = [[NetworkManager allocinit];

    });

    return sSharedInstance;

}

 

- (NSURL *)smartURLForString:(NSString *)str

{

    NSURL *     result;

    NSString *  trimmedStr;

    NSRange     schemeMarkerRange;

    NSString *  scheme;

    

    assert(str != nil);

    

    result = nil;

    

    trimmedStr = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

    if ( (trimmedStr != nil) && ([trimmedStr length] != 0) ) {

        schemeMarkerRange = [trimmedStr rangeOfString:@"://"];

        

        if (schemeMarkerRange.location == NSNotFound) {

            result = [NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@", trimmedStr]];

        } else {

            scheme = [trimmedStr substringWithRange:NSMakeRange(0, schemeMarkerRange.location)];

            assert(scheme != nil);

            

            if ( ([scheme compare:@"ftp"  options:NSCaseInsensitiveSearch] == NSOrderedSame) ) {

                result = [NSURL URLWithString:trimmedStr];

            } else {

                // It looks like this is some unsupported URL scheme.

            }

        }

    }

    

    return result;

}

 

- (BOOL)isImageURL:(NSURL *)url

{

    BOOL        result;

    NSString *  extension;

    

    assert(url != nil);

    

    extension = [url pathExtension];

    result = NO;

    if (extension != nil) {

        result = ([extension caseInsensitiveCompare:@"gif"] == NSOrderedSame)

        || ([extension caseInsensitiveCompare:@"png"] == NSOrderedSame)

        || ([extension caseInsensitiveCompare:@"jpg"] == NSOrderedSame);

    }

    return result;

}

 

- (void)didStartNetworkOperation

{

    // If you start a network operation off the main thread, you'll have to update this code

    // to ensure that any observers of this property are thread safe.

    assert([NSThread isMainThread]);

    self.networkOperationCount += 1;

}

 

- (void)didStopNetworkOperation

{

    // If you stop a network operation off the main thread, you'll have to update this code

    // to ensure that any observers of this property are thread safe.

    assert([NSThread isMainThread]);

    assert(self.networkOperationCount > 0);

    self.networkOperationCount -= 1;

}

 

@end

 
I removed some code from this file that was related to their demo project and not nice for general purpose use.

iOS: SUDZC sort SOAPArray with sortUsingDescriptors

The generated code from SUDZC lacks the function available from NSMutableArray: sortUsingDescriptors.

So I added this to the SOapArray class.

In SoapArray.h add:

- (void)sortUsingDescriptors:(NSArray *) sortDescriptors;

In SoapArray.m add:

-(void)sortUsingDescriptors:(NSArray *)sortDescriptors

{

    return [self.items sortUsingDescriptors:sortDescriptors];

}


iOS - SOAP - SUDZC - Headers (implement security and passing variables via headers)

When you are working with a WCF back end service, you will find development a really joy. We have a website calling the business layer below the WCF services. Thats working great. The instancing of this layer is done per session. So every ASP.Net session gets an instance of this layer, allowing the session to set variables once and reuse them during the session. For example, loggedin user etc...

Now I have a iOS device that wants to consume this service. To do so I created WCF services and transfer the data via SOAP protocol. (I know not JSON, but for some reasons I choose SOAP). 

Now this all is working fine. Until I started testing with more then 1 client. I noticed that the WCF services only could support PerCall or Singleton. PerSession was just not working as I expected. It was working as perCall. So each time an instance was taken. The business layer was used as singleton and reused by all calls. So I had to find a way to identify each call. From which device it was coming, what user made the call and some more parameters. Even a option to implement security. 

The way to solve this was using SOAP Headers. There you can put variable values and read them on the server. SUDZC already generates the code for you, now its up to you to implement it. I will start with iOS implementation and then show how to read the headers on a .Net server.

We will pass the device name in the header. This is a really simple implementation. Just go to the init of your service and add a new dictionary to the header. 

 

- (id) init

{

    if(self = [super init])

    {

        ...

        

        self.namespace = @"http://tempuri.org/";

        

        self.headers = [NSMutableDictionary dictionaryWithObject:[NSMutableDictionary dictionaryWithObject:[UIDevice currentDevice].name forKey:@"SecurityToken"] forKey:@"Authentication"];

        

        ...

        self.logging = NO;

    }

    return self;

}

So thats it really. Here you can see the generated SOAP message with the header.

<?xmlversion="1.0"encoding="utf-8"?>

<soap:Envelopexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"

               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns="http://tempuri.org/"

               xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays" >

  <soap:Header>

    <Authentication>

      <SecurityToken>iPad mderaeve</SecurityToken>

    </Authentication>

  </soap:Header>

  <soap:Body>

    <GetShiftPlanForDate>

      <date>2013-01-25T00:00:00.000</date>

      <shift>1</shift>

    </GetShiftPlanForDate>

  </soap:Body>

</soap:Envelope>

Thats all there is on iOS side, now we need to read this on our .Net server and handle it.

I made a small function that you can call in each WCF function:

public void GetMESHeaders(OperationContext operationContext)

    {

        string token = null;

 

        // Look at headers on incoming message.

        for (int i = 0; i < OperationContext.Current.IncomingMessageHeaders.Count; i++)

        {

            MessageHeaderInfo h = OperationContext.Current.IncomingMessageHeaders[i];

 

            // For any reference parameters with the correct name.

            if (h.Name == "Authentication")

            {

                // Read the value of that header.

                XmlReader xr = OperationContext.Current.IncomingMessageHeaders.GetReaderAtHeader(i);

                xr.ReadToDescendant("SecurityToken");

                //Set the device name on the Process layer.

                PL.DeviceName = xr.ReadElementContentAsString();

                //Check if the object is allowed

            }

        }

    }

 And thats all, really easy, still it can take some time to find this out. This way you can really customize the exchange of data and control it very good.


iOS: Programatically select a row in a tableview

Very simple thing, very simple code. Just putting it here for future use. Mind you that when you preform the selectRowAtIndexPath method, the didSelectRowAtIndexPath is not called. So you will need to trigger this manually.

Here's the code for how to select a row in your UITableView from code:

NSIndexPath * indexPath = [NSIndexPathindexPathForRow:0inSection:0];

[self.tableViewselectRowAtIndexPath:indexPath animated:YESscrollPosition:UITableViewScrollPositionNone];