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

Use of UIApplication.shared prevents usage within ios extensions #47

Open
fuji7l opened this issue May 5, 2017 · 5 comments
Open

Use of UIApplication.shared prevents usage within ios extensions #47

fuji7l opened this issue May 5, 2017 · 5 comments

Comments

@fuji7l
Copy link

fuji7l commented May 5, 2017

IOS extensions like share, document provider, etc do not allow access to UIAplication.shared.

This results in a compile time error due to

PasscodeLockPresenter.swift line 55.

@ziogaschr
Copy link
Collaborator

This is very interesting and I will probably run into the same bottleneck very soon.
@fuji7l can you make a PR on this? If you also add a demo (or enhance the existing one), I will jump in, review and merge once you nudge me.

Thanks

@fuji7l
Copy link
Author

fuji7l commented Jun 5, 2017

I solved it, but it's probably not the ideal way, not even sure a pull request would work with this. It's not ideal, not fully tested, but I've made progress with this so far. I honestly think some documentation on this would be better but after it's tested further. Especially since with different targets for different classes, it's not exactly something I would suggest baking in the repo.

You could add the #ifdef to the repo, but then you'd need to document what macro/define I as the user would need to set in my target's build settings. I believe AFNetworking does this, maybe you can use the same define as they do.

Again, I haven't tested this further than seeing that it compiles and runs. There may be some functions/features I'm not aware of where this breaks/falls apart. Also, the keyboard hiding code you have will not execute on an extension, I'm not sure how to address that.

With that said, here's what I did:

I edited PasscodeLockPresenter.swift to make the toggle method overridable.

open func toggleKeyboardVisibility(hide: Bool) {
        NSLog("Not implemented");
    }

Then I override that method in my class that implements PasscodeLockPresenter and added some C compile flags -DTARGET_APP_EXTENSION and macro TARGET_APP_EXTENSION=1 under the build settings for my app extensions.

class PasscodeDisplay: PasscodeLockPresenter {
    .... 
    override func toggleKeyboardVisibility(hide: Bool) {
        #if !TARGET_APP_EXTENSION
        if let keyboardWindow = UIApplication.shared.windows.last,
            keyboardWindow.description.hasPrefix("<UIRemoteKeyboardWindow")
        {
            keyboardWindow.alpha = hide ? 0.0 : 1.0
        }
        #endif
    }
}

Then for repository which requires "group" capabilities:

UserDefaultsPasscodeRepository: PasscodeRepositoryType {
    fileprivate lazy var defaults: UserDefaults = {
        
        return UserDefaults(suiteName: "group.com.my.security");
    }()!
}

And a special display class for extensions since the window object cannot be retrieved.

class PasscodeExtensionDisplay {
      
    var mConfig: PasscodeLockConfigurationType;
    var mPasscodeLockVC: PasscodeLockViewController;
    var mPasscodePresented = false;
    fileprivate lazy var mPasscodeLockWindow: UIWindow = {
        
        let window = UIWindow(frame: UIScreen.main.bounds)
        
        window.windowLevel = 0
        window.makeKeyAndVisible()
        
        return window
    }()
    
    init(configuration: PasscodeLockConfigurationType) {
        mConfig = configuration;
        mPasscodeLockVC = PasscodeLockViewController(state: .enterPasscode, configuration: configuration);
    }

    func shouldPresentPasscodeLock() -> Bool {
        guard mConfig.repository.hasPasscode else { NSLog("hasPasscode = false"); return false }
        guard !mPasscodePresented else { NSLog("isPresented = true");  return false; }
        return true;
        
    }
    
    func presentPasscodeLock() {
        guard mConfig.repository.hasPasscode else { return }
        guard !mPasscodePresented else { return }
        
        mPasscodePresented = true;
        let userDismissCompletionCallback = mPasscodeLockVC.dismissCompletionCallback
        
        mPasscodeLockVC.dismissCompletionCallback = { [weak self] in
            
            userDismissCompletionCallback?()
            
            self?.dismissPasscodeLock()
        }

    }
    func dismissPasscodeLock(animated: Bool = true) {
        
        mPasscodePresented = false
        
        if animated {
            UIView.animate(
                withDuration: 0.5,
                delay: 0,
                usingSpringWithDamping: 1,
                initialSpringVelocity: 0,
                options: [.curveEaseInOut],
                animations: { [weak self] in
                    
                    self?.mPasscodeLockWindow.alpha = 0
                },
                completion: { [weak self] _ in
                    
                    self?.mPasscodeLockWindow.windowLevel = 0
                    self?.mPasscodeLockWindow.rootViewController = nil
                    self?.mPasscodeLockWindow.alpha = 1
                }
            )
        } else {
            mPasscodeLockWindow.windowLevel = 0
            mPasscodeLockWindow.rootViewController = nil
        }
    }
}

And finally, in my UIViewController for the extension (e.g., share):

    lazy var mPassCode: PasscodeExtensionDisplay = {
        let conf = PasscodeLockConfig();
        return PasscodeExtensionDisplay(configuration: conf);
    }();
    override func viewWillAppear() {
        super.viewWillAppear()
            if (mPassCode.shouldPresentPasscodeLock()) {
                self.present(mPassCode.mPasscodeLockVC, animated: true, completion: nil);
            } else {
                NSLog("ShareVC shouldPresent returned false")
            }
   }
}

It works for me in my limited testing with the Share extension using my own UIViewController (instead of SLComposeViewController or whatever the default is).

I'm not too sure how you could handle this and what a pull request would even look like with this. It's really just documentation and then supporting that documentation for app extensions like Share and Document Provider.

@ziogaschr
Copy link
Collaborator

Thanks for the shared info @fuji7l. Any suggestion, in code or documentation form, is welcomed. Feel free to make a PR. I wasn't able to test your code because of lack of time. If you can also add this in a demo, this will be great.

Otherwise, your advice above is also helpful, if somebody requests the same. I might have a look at some point.

@fuji7l
Copy link
Author

fuji7l commented Jun 6, 2017

I'll see what I can do. I only have access to a mac while at work, so I'll try to get something together - can't guarantee when though.

@yishilin14
Copy link

yishilin14 commented Jun 14, 2017

I am running into this now. Looking forward to see a solution for app extension in the repository!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants