MarkupKit 3.4 Released

MarkupKit 3.4 is now available for download and via Cocoapods. This release includes the following updates:

  • Add baseline spacing support to LMColumnView. Arranged subviews can now be spaced vertically according to their baselines rather than their bounding rectangles. Additionally, system spacing can now be used in both row and column views in iOS 11 and later.

  • Add support for directional layout margins. Callers can now define locale-aware layout margins using the layoutMarginLeading and layoutMarginTrailing properties MarkupKit adds to UIView. In iOS 11 and later, these properties map directly to the view's directionalLayoutMargins. In iOS 10 and earlier, the current text direction (left-to-right or right-to-left) is used to dynamically apply the values.

  • Add LMTableViewHeaderFooterView class. iOS 11 appears to resolve issues associated with self-sizing table view header/footer views. As a result, it is now possible to provide an LMTableViewHeaderFooterView class for hosting custom section header and footer content.

  • Add support for custom string tables. In addition to bundleForStrings, LMViewBuilder will now also look for a tableForStrings method on the document owner. This method can be used to customize the string table used to resolve localized string references, and can be used either with or without bundleForStrings.

  • Add support for named colors in asset catalogs. In addition to UIColor constants and color table values, named colors can now refer to color sets defined in asset catalogs in iOS 11 and later.

  • Drop support for bi-directional binding. Internally, MarkupKit bindings are implemented using key-value observing (KVO). While app-specific classes reliably support KVO, UIKit view types are not guaranteed to, which can cause confusion or lead to bugs. Allowing bindings from owner to view only eliminates ambiguity while still supporting the model view-view-model (MVVM) design pattern.

For more information, see the project README.

HTTP-RPC 4.2 Released

HTTP-RPC 4.2 is now available for download, as well as via Cocoapods for iOS and Maven for Java:

<dependency>
    <groupId>org.httprpc</groupId>
    <artifactId>httprpc</artifactId>
    <version>...</version>
</dependency>

This release includes the following updates:

  • Refine invocation methods for Swift. Invocation methods are now parameterized, so it is no longer necessary to cast the return value to the desired type:
    open func invoke<T>(_ method: String, path: String, 
        resultHandler: @escaping (T?, Error?) -> Void) -> URLSessionTask? { ... }
    
    open func invoke<T>(_ method: String, path: String, arguments: [String: Any], 
        resultHandler: @escaping (T?, Error?) -> Void) -> URLSessionTask? { ... }

    For example:

    serviceProxy.invoke("GET", path: "/example") { (result: UIImage?, error) in
        // Handle image result
    }
  • Add support for custom response handlers. Callers can now perform custom deserialization via a response handler callback. In Swift, the callback is a closure:
    open func invoke<T>(_ method: String, path: String, arguments: [String: Any],
        responseHandler: @escaping (Data, String) throws -> T?,
        resultHandler: @escaping (T?, Error?) -> Void) -> URLSessionTask? { ... }

    For example (using JSONDecoder to return a strongly typed result):

    serviceProxy.invoke("GET", path: "/example", arguments: [:], responseHandler: { data, contentType in
        let decoder = JSONDecoder()
    
        return try? decoder.decode(Example.self, from: data)
    }) { (result: Example?, error) in
        // Handle custom result
    }

    In Java, the callback is defined as follows:

    public interface ResponseHandler<V> {
        public V decode(InputStream inputStream, String contentType) throws IOException;
    }

    For example (using Jackson to return a strongly typed result):

    serviceProxy.invoke("GET", "/example", mapOf(), (inputStream, contentType) -> {
        ObjectMapper objectMapper = new ObjectMapper();
    
        return objectMapper.readValue(input, Example.class);
    }, (Example result, Exception exception) -> {
        // Handle custom result
    });    

For more information, see the project README.