Skip to content
This repository has been archived by the owner on Feb 17, 2021. It is now read-only.

Adding an Objective-C compatible API to LayoutKit #180

Merged
merged 26 commits into from Feb 15, 2018
Merged

Adding an Objective-C compatible API to LayoutKit #180

merged 26 commits into from Feb 15, 2018

Conversation

staguer
Copy link
Contributor

@staguer staguer commented Feb 5, 2018

This is related to issue #178 regarding adding support for Objective-C consumers.
This objc-api branch is now ready for review.

Two of the open questions are:

  1. How do we make these long initializers more convenient for use from Objective-C? I have several options in mind.
  2. Should we make these new classes unavailable from Swift somehow? My understanding is that there's a way to use pods to exclude some directories at import time?

The builders for (1) can be added as a separate follow-up change.
Currently working on figuring out what we can do for (2).

@nicksnyder
Copy link
Collaborator

  1. My guess is that the best thing to do will be to make initializers take a single object. That object will have instance properties for the properties that you would normally pass in. This will save you from making a ridiculous number of initializer variants.
    e.g.
    LOKLinearLayoutOptions *linearLayoutOptions = [LOKLinearLayoutOptions new];
    linearLayoutOptions.arrangement = /* */
    linearLayoutOptions.size = CGSizeMake(10, 10);
    LOKLinearLayout *linearLayout = [LOKLinearLayout newWithOptions:options];
  2. I don't remember if this is possible or how to do it. If it is possible, it seems like it would be nice to do to avoid confusion in Swift.

@staguer
Copy link
Contributor Author

staguer commented Feb 12, 2018

This PR should be ready for review now. Two things are still missing from it: documentation comments and some support for splitting out the ObjC support code. I'll be working on the first one and investigating the second one.

@staguer
Copy link
Contributor Author

staguer commented Feb 12, 2018

Regarding question (2) of how to avoid the overhead of this new code for Swift consumers, @chenxiao0228 's suggestion is to make a separate GitHub repo and Cocoapod named something like LayoutKit-objc. These wrapper classes and sample app in this PR would not go into this LayoutKit repo but into the new LayoutKit-objc repo/pod and the new pod would import the current LayoutKit pod. Swift consumers will continue to use this repo/pod without incurring the weight of this new code.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
ViewController* viewController = [[ViewController alloc] init];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on our style guideline, we should have a space between ViewController and *, no space between * and ViewController. Please also apply the rest of them.

In this case, the style should like ViewController *viewController = [[ViewController alloc] init];

3.4 Pointer spacing
Pointers should have a space between the type and the *. There should be no space between variable names and the *.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll fix that.

#import <LayoutKit/LayoutKit-Swift.h>

@interface RotationLayout: NSObject <LOKLayout>
-(nonnull instancetype)initWithSublayout:(nonnull id<LOKLayout>)sublayout alignment:(nullable LOKAlignment *)alignment viewReuseId:(nullable NSString *)viewReuseId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you reorder the methods under the property declarations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll fix that.


@interface RotationLayout: NSObject <LOKLayout>
-(nonnull instancetype)initWithSublayout:(nonnull id<LOKLayout>)sublayout alignment:(nullable LOKAlignment *)alignment viewReuseId:(nullable NSString *)viewReuseId;
@property (readonly, nonatomic, nonnull) id<LOKLayout> sublayout;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also reorder attributes based on 3.5.1.1 Attribute ordering. Also the others.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll fix that.

@interface RotationView: UIView
@end
@implementation RotationView
-(instancetype)init {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 3.9 Method declarations and definitions

One space should be used between the - or + and the return type. Also the others.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll fix that.

}

- (void)configureWithBaseTypeView:(nonnull UIView *)baseTypeView {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this method empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implements the method @objc func configure(baseTypeView: View) on the LOKLayout protocol.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think if we should move this method to optional?

// [self.view addSubview:tableView];
// self.adapter = [[LOKReloadableViewLayoutAdapter alloc] initWithTableView:tableView];
// tableView.delegate = self.adapter;
// tableView.dataSource = self.adapter;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you comment out those code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I was using it to verify that the wrapper worked for UITableView, I'll remove it.

// tableView.delegate = self.adapter;
// tableView.dataSource = self.adapter;

LOKSizeLayout *cellLayout = [[LOKSizeLayout alloc] initWithMinWidth:self.view.bounds.size.width
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered creating a few convenience initializers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are too many parameters with default properties, so making convenience initializers even for the common combinations would be too many. A builder pattern will be more appropriate and that's coming in a separate change.

@@ -30,10 +30,21 @@ open class BaseLayout<V: View> {
return config != nil
}

private let viewClass: View.Type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this is objective-c doesn't support V?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objective C has some support for generics but it's mostly for defining neat interfaces for Swift, and some mild type checking on Obj C side, not nearly as full featured as Swift generics.

public class LOKBaseLayout: NSObject, LOKLayout {
let layout: Layout

init(layout: Layout) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep all init internally for wrapper use?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.


import UIKit

@objc public class LOKButtonLayout: LOKBaseLayout {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering why we can't directly create objective-c classes for wrappers? With objective-c wrappers, it might be much faster with generating swift interfaces. Also, we can separate them to different pod, so the LayoutKit is purely swift LayoutKit without objc support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift was chosen for these wrappers since this codebase already uses Swift and for simplicity I'd like to keep it single-language. I doubt there would much difference in speed, either at editing, build, or run time.

Regarding Pods, I'm planning to exclude these wrappers from the current Pod and include them in a new Pod.

@jingwei-huang1
Copy link
Contributor

Additional to @chenxiao0228 comment, we can still keep them in the same repo, but 2 x pods and that would be easier to manage.

@jingwei-huang1
Copy link
Contributor

Also, we need to setup the test for wrappers because the current CI doesn't compile objective-c wrappers

Sergei Taguer added 3 commits February 13, 2018 14:12
 $ pod lib lint

 -> LayoutKit (6.0.1)

LayoutKit passed validation.
 -> LayoutKitObjC (6.0.1)

LayoutKitObjC passed validation.

import CoreGraphics

class ReverseWrappedLayout: Layout {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems ReverseWrappedLayout.swift and WrappedLayout isn't being used for the public. Could you create a Internal folder for those two classes?

}

- (void)configureWithBaseTypeView:(nonnull UIView *)baseTypeView {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think if we should move this method to optional?

image: UIImage?,
imageSize: CGSize,
font: UIFont? = nil,
contentEdgeInsets: NSValue?, // UIEdgeInsets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of NSValue, we might consider wrapping it a class. To compare with NSValue, it would be easier to understand and configure.

@staguer staguer merged commit 0113630 into master Feb 15, 2018
@staguer staguer deleted the objc-api branch February 27, 2018 01:00
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants