Posts Tagged ‘Swift’

Swift Property Wrapper for Cancellable

Saturday, July 4th, 2020

With SwiftUI Apple developed a new and modern way to realize user interfaces for apps. It’s faszinating how the language Swift itself with new features and all the frameworks perfectly fit together to form the capabilities of SwiftUI as a whole. One important piece of that puzzle is the Combine framework that provides a kind of “reactive” API. If you write view model code for SwiftUI, you regulary have to deal with Cancellables and you end up with the following principle over and over again:

class Someclass : ObservableObject {

    private var someCancellable: AnyCancellable? {
        didSet {
            oldValue?.cancel()
        }
    }

    private var anotherCancellable: AnyCancellable? {
        didSet {
            oldValue?.cancel()
        }
    }

    deinit {
        self.someCancellable?.cancel()
        self.anotherCancellable?.cancel()
    }
}

This is a perfect example to make use of a Property Wrapper! To be honest, I am wondering why Apple does not provide this out of the box. Property Wrappers are a way in Swift to define a customized behaviour for properties. Here’s my solution to improve and automate the repetitive code from above:

import Combine

@propertyWrapper
class AutoCancel<Value> where Value: Cancellable {
    var wrappedValue: Value? {
        didSet {
            // cancel the old Cancellable whenever a new is set:
            oldValue?.cancel()
        }
    }

    deinit {
        // cancel the Cancellable on deinit
        self.wrappedValue?.cancel()
    }
}

It’s short and easy, but has a huge impact: Now you can replace the initial verbose code from above with the following:

class Someclass : ObservableObject {
    @AutoCancel private var someCancellable: AnyCancellable?
    @AutoCancel private var anotherCancellable: AnyCancellable?
}

That’s it! The code size reduced dramatically and it is much easier to type and to read, and it is also less error-prone because you don’t have to think about cancelling these properties in the deinit block anymore.

Have fun and happy coding!

Objective-C Blocks as Tuples Replacement

Thursday, August 14th, 2014

When programming you sometimes want to return multiple values from a function. The problem is that many languages only support one return value per function. A commonly used solution is to pass values by reference as input parameters to the function and “misuse” these as output parameters. Some languages (or libraries) – e. g. Swift – provide a tuple type where you can pack multiple values together so you can return multiple values as a tuple. You can also put your return values in some other kind of container, e. g. a dictionary or an array. All these solutions have their pitfalls, mostly some kind of loosing the type of the return values or having to box and unbox primitive values or just being cumbersome or hard to read.

Recently I came up with another idea how to get multiple return values from a function: If the used language supports some sort of closures (e. g. blocks in Obj-C) simply let the caller pass a closure that receives the return values with their proper types and call that closure to return the values.

An example in Objective-C:
Let’s create a class extension to NSArray that assumes the array just contains NSString objects and searches for the string that appears most often in that array. The result values are the number of occurrences of the string and the string itself.

@interface NSArray (MyExtension)
- (void)findMostUsedString:(void (^)(int count, NSString *string))result;
@end

@implementation NSArray (MyExtension)

- (void)findMostUsedString:(void (^)(int count, NSString *string))result
{
	// do the heavy calculation:
	int maxCount = 0;
	NSString *maxString = nil;
	@autoreleasepool {
		NSMutableDictionary *counters = [NSMutableDictionary dictionary];
		for(NSString *string in self) {
			@autoreleasepool {
				int count = [counters[string] intValue] + 1;
				counters[string] = @(count);
				if (count > maxCount) {
					maxCount = count;
					maxString = string;
				}
			}
		}
	}

	// return the result consisting of multiple values
	// by calling the passed block:
	result(maxCount, maxString);
}

@end

Now you can use that function returning two values as simple as that:

NSArray *array = @[@"foo", @"bar", @"foo", @"foobar", @"42", @"foo"];
[array findMostUsedString:^(int count, NSString *string) {
	NSLog(@"Found \"%@\" %d times", string, count);
}];

Output is:

Found "foo" 3 times