diff --git a/packages/twitter/README.md b/packages/twitter/README.md
index 13572c13..1c021e91 100644
--- a/packages/twitter/README.md
+++ b/packages/twitter/README.md
@@ -19,32 +19,26 @@ Here is how callbacks would look like:
### iOS
-
-Configure Info.Plist like below, replace `consumerKey` tag with your own key:
-
```xml
CFBundleURLTypes
CFBundleURLSchemes
- twitterkit-
+ yourscheme>
-LSApplicationQueriesSchemes
-
- twitter
- twitterauth
-
```
```ts
import { Twitter, TwitterSignIn } from '@nativescript/twitter';
-TwitterSignIn.init(TWITTER_COMSUMER_KEY, TWITTER_CONSUMER_SECRET);
+Twitter.callback = 'yourscheme://';
+
+Twitter.init(TWITTER_COMSUMER_KEY, TWITTER_CONSUMER_SECRET);
-Twitter.logIn()
+TwitterSignIn.logIn()
.then((session) => {
// session.authToken
// session.authTokenSecret
diff --git a/packages/twitter/index.android.ts b/packages/twitter/index.android.ts
index 40f957d3..b04d7191 100644
--- a/packages/twitter/index.android.ts
+++ b/packages/twitter/index.android.ts
@@ -63,6 +63,19 @@ export class TwitterUser implements ITwitterUser {
get android() {
return this.native;
}
+
+ toJSON() {
+ return {
+ formattedScreenName: this.formattedScreenName,
+ isProtected: this.isProtected,
+ isVerified: this.isVerified,
+ name: this.name,
+ profileImageUrl: this.profileImageUrl,
+ profileUrl: this.profileUrl,
+ screenName: this.screenName,
+ userId: this.userId
+ }
+ }
}
export class Twitter {
@@ -180,7 +193,7 @@ export class TwitterSignIn {
}
sessionManager.clearActiveSession();
- } catch (e) {}
+ } catch (e) { }
}
static getCurrentUser() {
diff --git a/packages/twitter/index.d.ts b/packages/twitter/index.d.ts
index 70dbecd3..6a33858c 100644
--- a/packages/twitter/index.d.ts
+++ b/packages/twitter/index.d.ts
@@ -39,6 +39,7 @@ export declare class Session implements ISession {
}
export declare class Twitter {
+ static callback: string;
static init(consumerKey: string, consumerSecret: string);
}
diff --git a/packages/twitter/index.ios.ts b/packages/twitter/index.ios.ts
index 7ce55ea5..dbeda664 100644
--- a/packages/twitter/index.ios.ts
+++ b/packages/twitter/index.ios.ts
@@ -1,3 +1,4 @@
+import { Application, Device } from '@nativescript/core';
import { ISession, ITwitterUser } from './common';
export class TwitterError extends Error {
@@ -14,9 +15,10 @@ export class TwitterError extends Error {
}
let appDelegateInitialized = false;
-let appDelegate: TwitterAppDelegateImpl;
-@NativeClass
+let appDelegate;
+
@ObjCClass(UIApplicationDelegate)
+@NativeClass
class TwitterAppDelegateImpl extends UIResponder implements UIApplicationDelegate {
static get sharedInstance() {
if (!appDelegate) {
@@ -26,14 +28,27 @@ class TwitterAppDelegateImpl extends UIResponder implements UIApplicationDelegat
}
applicationOpenURLOptions(app: UIApplication, url: NSURL, options: NSDictionary): boolean {
- return TWTRTwitter.sharedInstance().applicationOpenURLOptions(app, url, options);
+ TNSTwitter.handleOpenURL(url.absoluteString, Twitter.callback, false);
+ return true;
+ }
+
+}
+
+
+if (parseFloat(Device.sdkVersion) >= 13) {
+ (TwitterAppDelegateImpl).prototype.applicationConfigurationForConnectingSceneSessionOptions = (application: UIApplication, connectingSceneSession: UISceneSession, options: UISceneConnectionOptions) => {
+ return UISceneConfiguration.configurationWithNameSessionRole(
+ "Default Configuration",
+ connectingSceneSession.role
+ )
}
}
+
export class TwitterUser implements ITwitterUser {
- #native: TWTRUser;
- static fromNative(user: TWTRUser) {
- if (user instanceof TWTRUser) {
+ #native: any;
+ static fromNative(user: any) {
+ if (user) {
const usr = new TwitterUser();
usr.#native = user;
return usr;
@@ -42,15 +57,15 @@ export class TwitterUser implements ITwitterUser {
}
get formattedScreenName(): string {
- return this.native.formattedScreenName;
+ return this.native.screen_name;
}
get isProtected(): boolean {
- return this.native.isProtected;
+ return this.native.protected;
}
get isVerified(): boolean {
- return this.isVerified;
+ return this.native.verified;
}
get name(): string {
@@ -58,19 +73,19 @@ export class TwitterUser implements ITwitterUser {
}
get profileImageUrl(): string {
- return this.native.profileImageURL;
+ return this.native.profile_image_url_https;
}
get profileUrl(): string {
- return this.native.profileURL?.absoluteString;
+ return this.native.url;
}
get screenName(): string {
- return this.native.screenName;
+ return this.native.screen_name;
}
get userId(): string {
- return this.native.userID;
+ return this.native.id_str;
}
get native() {
@@ -80,12 +95,26 @@ export class TwitterUser implements ITwitterUser {
get ios() {
return this.native;
}
+
+
+ toJSON() {
+ return {
+ formattedScreenName: this.formattedScreenName,
+ isProtected: this.isProtected,
+ isVerified: this.isVerified,
+ name: this.name,
+ profileImageUrl: this.profileImageUrl,
+ profileUrl: this.profileUrl,
+ screenName: this.screenName,
+ userId: this.userId
+ }
+ }
}
export class Session implements ISession {
- #native: TWTRSession;
- static fromNative(ts: TWTRSession) {
- if (ts instanceof TWTRSession) {
+ #native: any;
+ static fromNative(ts) {
+ if (ts) {
const session = new Session();
session.#native = ts;
return session;
@@ -117,9 +146,12 @@ export class Session implements ISession {
}
}
+
export class Twitter {
+ static _twitter: TNSTwitter;
+ static callback: string;
static init(consumerKey: string, consumerSecret: string) {
- TWTRTwitter.sharedInstance().startWithConsumerKeyConsumerSecret(consumerKey, consumerSecret);
+ this._twitter = TNSTwitter.alloc().init(consumerKey, consumerSecret);
if (!appDelegateInitialized) {
GULAppDelegateSwizzler.proxyOriginalDelegate();
@@ -132,38 +164,79 @@ export class Twitter {
export class TwitterSignIn {
static logIn() {
return new Promise((resolve, reject) => {
- TWTRTwitter.sharedInstance().logInWithCompletion((session, error) => {
- if (session) {
- resolve(Session.fromNative(session));
+ Twitter._twitter.authorizeWithBrowser(this.topViewController, Twitter.callback, (user, error) => {
+ if (user) {
+ try {
+ resolve(Session.fromNative(JSON.parse(user)));
+ } catch (e) {
+ reject(TwitterError.fromNative(e));
+ }
} else {
reject(TwitterError.fromNative(error));
}
- });
+ })
});
}
- static logOut() {
- const store = TWTRTwitter.sharedInstance().sessionStore;
- if (store) {
- const userId = store.session()?.userID;
- store.logOutUserID?.(userId);
- }
- }
+
+ static logOut() { }
static getCurrentUser(): Promise {
return new Promise((resolve, reject) => {
- const store = TWTRTwitter.sharedInstance().sessionStore;
- if (store) {
- const userId = store.session()?.userID || '';
- TWTRAPIClient.clientWithCurrentUser().loadUserWithIDCompletion(userId, (user, error) => {
- if (error) {
- reject(TwitterError.fromNative(error));
- } else {
- resolve(TwitterUser.fromNative(user));
+ Twitter._twitter.verifyUser(false, false, true, (user, error) => {
+ if (error) {
+ reject(TwitterError.fromNative(error));
+ } else {
+ try {
+ resolve(TwitterUser.fromNative(JSON.parse(user)));
+ } catch (e) {
+ reject(TwitterError.fromNative(e));
}
- });
- } else {
- resolve(null);
- }
+
+ }
+ })
});
}
+
+
+
+ private static get topViewController(): UIViewController | undefined {
+ const root = this.rootViewController;
+ if (!root) {
+ return undefined;
+ }
+ return this.findTopViewController(root);
+ }
+
+ private static get rootViewController(): UIViewController | undefined {
+ const keyWindow = UIApplication.sharedApplication.keyWindow;
+ return keyWindow ? keyWindow.rootViewController : undefined;
+ }
+
+ private static findTopViewController(root: UIViewController): UIViewController | undefined {
+ const presented = root.presentedViewController;
+ if (presented != null) {
+ return this.findTopViewController(presented);
+ }
+ if (root instanceof UISplitViewController) {
+ const last = root.viewControllers.lastObject;
+ if (last == null) {
+ return root;
+ }
+ return this.findTopViewController(last);
+ } else if (root instanceof UINavigationController) {
+ const top = root.topViewController;
+ if (top == null) {
+ return root;
+ }
+ return this.findTopViewController(top);
+ } else if (root instanceof UITabBarController) {
+ const selected = root.selectedViewController;
+ if (selected == null) {
+ return root;
+ }
+ return this.findTopViewController(selected);
+ } else {
+ return root;
+ }
+ }
}
diff --git a/packages/twitter/package.json b/packages/twitter/package.json
index 44b3f34c..e10c821b 100644
--- a/packages/twitter/package.json
+++ b/packages/twitter/package.json
@@ -1,6 +1,6 @@
{
"name": "@nativescript/twitter",
- "version": "1.0.0-alpha.3",
+ "version": "1.0.0-alpha.7",
"description": "Twitter for your NativeScript applications",
"main": "index",
"typings": "index.d.ts",
diff --git a/packages/twitter/platforms/ios/Podfile b/packages/twitter/platforms/ios/Podfile
index 9d5fc5c9..bafb039d 100644
--- a/packages/twitter/platforms/ios/Podfile
+++ b/packages/twitter/platforms/ios/Podfile
@@ -1,2 +1,3 @@
-pod 'TwitterKit5'
+platform :ios, '10.0'
+pod 'Swifter', :git => 'https://github.com/mattdonnelly/Swifter.git', :tag => "2.5.0"
pod 'GoogleUtilities', '~> 7.5'
\ No newline at end of file
diff --git a/packages/twitter/platforms/ios/src/TNSTwitter.swift b/packages/twitter/platforms/ios/src/TNSTwitter.swift
new file mode 100644
index 00000000..65a73209
--- /dev/null
+++ b/packages/twitter/platforms/ios/src/TNSTwitter.swift
@@ -0,0 +1,106 @@
+import Foundation
+import Swifter
+import SafariServices
+import AuthenticationServices
+
+
+
+struct TNSSession: Codable {
+ var authToken: String?
+ var authTokenSecret: String?
+ var userName: String?
+ var userId: String?
+
+ init(token: Credential.OAuthAccessToken){
+ authToken = token.key
+ authTokenSecret = token.secret
+ userName = token.screenName
+ userId = token.userID
+ }
+}
+
+
+
+@objcMembers
+@objc(TNSTwitter)
+public class TNSTwitter: NSObject {
+ private var swifter: Swifter
+ private var controller: UIViewController?
+ private let encoder = JSONEncoder()
+
+ public static func handleOpenURL(_ url: String,_ callbackURL: String,_ isSSO: Bool) -> Bool {
+ return Swifter.handleOpenURL(URL(string: url)!, callbackURL: URL(string: callbackURL)!, isSSO: isSSO)
+ }
+
+ public init?(_ consumerKey: String, _ consumerSecret: String) {
+ guard !consumerKey.isEmpty || !consumerSecret.isEmpty else {return nil}
+ swifter = Swifter(consumerKey: consumerKey, consumerSecret: consumerSecret)
+ }
+
+
+ private func handleSession(_ token: Credential.OAuthAccessToken, _ callback: @escaping ((String?, NSError?) -> Void)){
+ let session = TNSSession(token: token)
+ do {
+ callback(String(data: try self.encoder.encode(session), encoding: .utf8) ?? "", nil)
+ }catch {
+ callback(nil, error as NSError)
+ }
+ }
+
+ public func verifyUser(
+ _ includeEntities: Bool,
+ _ skipStatus: Bool,
+ _ includeEmail: Bool,_ callback: @escaping ((String?, NSError?) -> Void)) {
+ swifter.verifyAccountCredentials(includeEntities: includeEntities, skipStatus: skipStatus, includeEmail: includeEmail) { user in
+ callback(String(describing: user), nil)
+ } failure: { error in
+ callback(nil, error as NSError)
+ }
+
+ }
+
+ public func authorizeWithSSO(_ callback: @escaping ((String?, NSError?) -> Void)){
+ swifter.authorizeSSO { token in
+ self.handleSession(token, callback)
+ } failure: { error in
+ callback(nil, error as NSError)
+ }
+ }
+
+ public func authorizeWithBrowser(_ controller: UIViewController,_ callbackURL: String, _ callback: @escaping ((String?, NSError?) -> Void)){
+ self.controller = controller
+ if #available(iOS 13.0, *) {
+ swifter.authorize(withProvider: self, callbackURL: URL(string: callbackURL)!) { token, response in
+ self.controller = nil
+ self.handleSession(token!, callback)
+ } failure: {error in
+ self.controller = nil
+ callback(nil, error as NSError)
+ }
+ }else {
+ swifter.authorize(withCallback: URL(string: callbackURL)!, presentingFrom: controller) { token, response in
+ self.controller = nil
+ self.handleSession(token!, callback)
+ } failure: {error in
+ self.controller = nil
+ callback(nil, error as NSError)
+ }
+ }
+ }
+
+}
+
+
+extension TNSTwitter: SFSafariViewControllerDelegate {
+ @nonobjc func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
+ controller.dismiss(animated: true, completion: nil)
+ }
+}
+
+
+@available(iOS 13.0, *)
+extension TNSTwitter: ASWebAuthenticationPresentationContextProviding {
+ public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
+ return controller!.view.window!
+ }
+}
diff --git a/packages/twitter/src-native/TwitterDemo/.idea/compiler.xml b/packages/twitter/src-native/TwitterDemo/.idea/compiler.xml
index 61a9130c..fb7f4a8a 100644
--- a/packages/twitter/src-native/TwitterDemo/.idea/compiler.xml
+++ b/packages/twitter/src-native/TwitterDemo/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/packages/twitter/src-native/TwitterDemo/.idea/gradle.xml b/packages/twitter/src-native/TwitterDemo/.idea/gradle.xml
index 85b7f0a0..0c43e68a 100644
--- a/packages/twitter/src-native/TwitterDemo/.idea/gradle.xml
+++ b/packages/twitter/src-native/TwitterDemo/.idea/gradle.xml
@@ -7,6 +7,7 @@
+