Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Objective C Storyboard swizzling #46

Merged
merged 20 commits into from
Aug 25, 2017
Merged

Conversation

mpdifran
Copy link
Member

@mpdifran mpdifran commented Apr 16, 2017

DO NOT MERGE: work in progress.

EDIT: It is now ready for merge once we update TravisCI.

if (self == [Storyboard class]) {

// Instantiate SwinjectStoryboard if UI/NSStoryboard is trying to be instantiated.
if [SwinjectStoryboard isCreatingStoryboardReference] {
Copy link
Member Author

Choose a reason for hiding this comment

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

Need to #import "SwinjectStoryboard-Swift.h" in order to access this Swift class method, but for some reason the header is not automatically generated.

Copy link
Member

Choose a reason for hiding this comment

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

@mpdifran, I'll reply shortly.

Copy link
Member

@yoichitgy yoichitgy Apr 20, 2017

Choose a reason for hiding this comment

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

Hi @mpdifran. I took time for my misunderstanding.

Usually a bridging header is required to access objc classes or methods from Swift. If you need to expose Swift class, you can just add @objc annotation. If the problem is that isCreatingStoryboardReference is defined as static var, you can change it to class var.

Am I missing the point of your comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, the bridging header is for accessing ObjC classes in Swift, but in this case, we're accessing a Swift class in ObjC. From what I've read online, there should be an automatically generated header named SwinjectStoryboard-Swift.h that we can import in our ObjC files.

But maybe adding the @objc to the class and changing the static var to a class var will work. I'll give that a shot.

@mpdifran
Copy link
Member Author

Updated to use @objc, but it's still having issues finding the SwinjectStoryboard Swift class:

/Users/mpdifran/Sandbox/SwinjectStoryboard/Sources/Storyboard+Swizzling.m:56:14: error: use of undeclared identifier 'SwinjectStoryboard'
    if ([SwinjectStoryboard isCreatingStoryboardReference]) {
         ^

@mpdifran mpdifran force-pushed the objCSwizzle branch 3 times, most recently from 38fbbe2 to 4fd7f18 Compare April 21, 2017 02:45
@yoichitgy
Copy link
Member

@mpdifran, I reproduced the issue. As you said, we needed to import SwinjectStoryboard-Swift.h which is specified in the project as Objective-C Generated Interface Header Name. I checked some stackoviewflow answers, but still I couldn't find a solution. I keep solving this problem.

@yoichitgy
Copy link
Member

I followed the section below in the Apple's official document, but still SwinjectStoryboard/SwinjectStoryboard-Swift.h cannot be imported...

  • "Importing Code from Within the Same Framework Target"
    • "Importing Swift into Objective-C"

https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html


#import "Storyboard+Swizzling.h"
#import <objc/runtime.h>
#import <SwinjectStoryboard/SwinjectStoryboard-Swift.h>
Copy link
Member Author

Choose a reason for hiding this comment

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

This seems to be the correct way to import the header.

@mpdifran
Copy link
Member Author

I'm still getting one warning on public override class func initialize() in SwinjectStoryboard.swift. Any ideas for how to solve that?

@yoichitgy
Copy link
Member

@mpdifran, I'm sorry I took time to respond.

Do you have any change since the last week? I will fetch your commit to check the warning on public override class func initialize(). Thanks😃

@mpdifran
Copy link
Member Author

Nope, haven't taken a look at it since.

@yoichitgy
Copy link
Member

@mpdifran Do you no longer have the error to import the header file? I still have error here in Storyboard+Swizzling.m.

#import <SwinjectStoryboard/SwinjectStoryboard-Swift.h>

@mpdifran
Copy link
Member Author

mpdifran commented May 1, 2017

Ah looks like I hadn't pushed up my recent changes. I just did a force push, try pulling down the branch again.

Also, correct, I am able to build the project fine; I don't see the header error.

@@ -26,7 +26,7 @@ class Container_SwinjectStoryboardSpec: QuickSpec {
expect(container.description) ==
"[\n"
+ " { Service: \(controllerType), Storyboard: SwinjectStoryboardTests.AnimalViewController, "
+ "Factory: ((Resolver, \(controllerType))) -> \(controllerType), ObjectScope: graph, InitCompleted: Specified }\n"
+ "Factory: (Resolver, \(controllerType)) -> \(controllerType), ObjectScope: graph, InitCompleted: Specified }\n"
Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure why, but this test was failing, and I needed to make this change in order to make it pass.

Copy link
Member

Choose a reason for hiding this comment

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

It's difference between Swift 3.0 and 3.1. Swift 3.1 fixed the doubly printed tuple: ((a, b))
Swinject/Swinject#252

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah ok cool.

@yoichitgy
Copy link
Member

Thanks @mpdifran👍 I confirmed the build passed with the swift header imported. I'm going to check the issue on public override class func initialize().

@yoichitgy
Copy link
Member

I'll investigate public override class func initialize() in SwinjectStoryboard.swift in this weekend😉

@mpdifran
Copy link
Member Author

Just did a rebase and force push to fix the merge conflicts. @yoichitgy I'll be around this weekend if you want some help tackling this.

@mpdifran mpdifran changed the title WIP: Objective C Storyboard swizzling Objective C Storyboard swizzling May 19, 2017
@mpdifran
Copy link
Member Author

I think I've fixed the last warning. Let me know if you approve of this implementation.

@mpdifran
Copy link
Member Author

Looks like the tests are failing. I'll take another look sometime soon.

@mpdifran
Copy link
Member Author

Fixed the failing test. Moved the + (void)load override into a category on SwinjectStoryboard instead of UIStoryboard in order to get the proper protocol conformance.

bundle storyboardBundleOrNil: Bundle?) -> SwinjectStoryboard {
return SwinjectStoryboard.create(name: name, bundle: storyboardBundleOrNil,
container: SwinjectStoryboard.defaultContainer)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This needed to be done because ObjC doesn't support Swift's default parameters (container: Resolver = SwinjectStoryboard.defaultContainer). These two methods are functionally equivalent to the single old method, so the API hasn't actually changed.

@yoichitgy
Copy link
Member

I'm sorry, but I'll continue working on Podspec in a few days 🙇

@mpdifran
Copy link
Member Author

@yoichitgy do you think you'll have a chance to fix the pod spec soon?

@yoichitgy
Copy link
Member

Sorry @mpdifran for my late response for this valuable pull request😱 I was too busy for a big project as my job to work on Swinject projects, but I got knowledge about podspec a lot in the project. I will try to solve the problem within a few weeks🏃

@yoichitgy
Copy link
Member

@mpdifran, I resolved the following issue on pod lib lint in this branch.

'SwinjectStoryboard/SwinjectStoryboard-Swift.h' file not found

Now I got new errors:

error: no known class method for selector 'isCreatingStoryboardReference'
error: no known class method for selector 'createReferencedWithName:bundle:'

I'm working on the issues now.

@yoichitgy
Copy link
Member

The cause of the new errors is this:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

Because the generated header for a framework target is part of the framework’s public interface, only declarations marked with the public or open modifier appear in the generated header for a framework target.

Swift methods and properties that are marked with the internal modifier and declared within a class that inherits from an Objective-C class are accessible to the Objective-C runtime. However, they are not accessible at compile time and do not appear in the generated header for a framework target.

For framework targets, only declarations with the public or open modifier appear in the generated header. You can still use Swift methods and properties that are marked with the internal modifier from within the Objective-C part of your framework, as long they are declared within a class that inherits from an Objective-C class. For more information on access-level modifiers, see Access Control in The Swift Programming Language (Swift 4).

…thod to Objective-C.

I actually want to revert this commit not to make the property and method public because they are not intended to be used by users of SwinjectStoryboard.
@yoichitgy
Copy link
Member

yoichitgy commented Aug 6, 2017

I solved the pod lib lint issues in this branch, but I want to revert the commit (e5d2043) not to expose those methods that should not be used by SwinjectStoryboard users.

@mpdifran, do you have any idea to revert the commit and solve the problem by another way?

(I made a pull request to merge my changes into your pull request.)

@mpdifran
Copy link
Member Author

mpdifran commented Aug 6, 2017

Let me take a look at it when I get a chance. Was this issue introduced due to the pod lib lint changes? I’ve been using this branch through Carthage with no issues...

@yoichitgy
Copy link
Member

yoichitgy commented Aug 6, 2017

I'm still not sure the cause of the problem that internal methods are not exposed to SwinjectStoryboard-Swift.h with CocoaPods. If pod lib lint experiences the problem, CocoaPods users will see the problem.

I didn't get the problem when I built SwinjectStoryboard project, but it might be reproducible if you install SwinjectStoryboard to an app project by Carthage. (I didn't try this yet.)

@mpdifran
Copy link
Member Author

mpdifran commented Aug 7, 2017

I'm currently using this branch in one of my apps through Carthage, so there's no problem on the Carthage side.

@yoichitgy
Copy link
Member

Thanks @mpdifran👍 I'll investigate podspec to make those method internal in this weekend.

@yoichitgy
Copy link
Member

I'm trying to resolve the issue by fixing directions of Swift/ObjC references.

@yoichitgy
Copy link
Member

@mpdifran, I made PR #67 taking over your PR to fix the installation issue. Your PR will be automatically merged when #67 is merged.

At the end, I ended up with performSelector to call internal class methods defined in Swift. Not perfect but we didn't expose unnecessary methods to users of SwinjectStoryboard.

Thank you for your investigation, fix and patience😃😃😃

Update of your pull request Swinject#46 to Swinject/SwinjectStoryboard
@yoichitgy
Copy link
Member

yoichitgy commented Aug 25, 2017

Thanks @mpdifran for merging my branch👍

@yoichitgy yoichitgy merged commit 285cb0b into Swinject:master Aug 25, 2017
@mpdifran mpdifran deleted the objCSwizzle branch August 25, 2017 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants