Skip to content

Commit

Permalink
Merge pull request #171 from lexrus/master
Browse files Browse the repository at this point in the history
支持微信 Universal Link
  • Loading branch information
nixzhu authored Aug 26, 2020
2 parents 4306b41 + 956eea8 commit aef678e
Show file tree
Hide file tree
Showing 15 changed files with 457 additions and 106 deletions.
10 changes: 10 additions & 0 deletions China.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
50FBA6D71BA2BBE900E69BB3 /* MonkeyKing.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 50FBA6CF1BA2BBE900E69BB3 /* MonkeyKing.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7840AF1D1C63167C00AD5256 /* pay.php in Resources */ = {isa = PBXBuildFile; fileRef = 7840AF1C1C63167C00AD5256 /* pay.php */; };
955654EB1EB89D5D009444FF /* TwitterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955654EA1EB89D5D009444FF /* TwitterViewController.swift */; };
D71A624424D313E5002B2977 /* MonkeyKing+WeChatUniversalLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71A624324D313E5002B2977 /* MonkeyKing+WeChatUniversalLink.swift */; };
EDD0A28C213E6F7B003EB202 /* WeChatActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0A28A213E6F7B003EB202 /* WeChatActivity.swift */; };
EDD0A28D213E6F7B003EB202 /* QQActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD0A28B213E6F7B003EB202 /* QQActivity.swift */; };
EDD0A28F213E6FB2003EB202 /* gif.gif in Resources */ = {isa = PBXBuildFile; fileRef = EDD0A28E213E6FB2003EB202 /* gif.gif */; };
Expand Down Expand Up @@ -103,6 +104,10 @@
50FBA6D31BA2BBE900E69BB3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7840AF1C1C63167C00AD5256 /* pay.php */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.php; path = pay.php; sourceTree = "<group>"; };
955654EA1EB89D5D009444FF /* TwitterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwitterViewController.swift; sourceTree = "<group>"; };
D71A624324D313E5002B2977 /* MonkeyKing+WeChatUniversalLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "MonkeyKing+WeChatUniversalLink.swift"; path = "Sources/MonkeyKing/MonkeyKing+WeChatUniversalLink.swift"; sourceTree = SOURCE_ROOT; };
D79F73B524920FBC00B4B883 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
D79F73B624920FBC00B4B883 /* MonkeyKing.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = MonkeyKing.podspec; sourceTree = "<group>"; };
D79F73B724920FBC00B4B883 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
EDD0A28A213E6F7B003EB202 /* WeChatActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeChatActivity.swift; sourceTree = "<group>"; };
EDD0A28B213E6F7B003EB202 /* QQActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QQActivity.swift; sourceTree = "<group>"; };
EDD0A28E213E6FB2003EB202 /* gif.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gif.gif; sourceTree = "<group>"; };
Expand Down Expand Up @@ -130,6 +135,9 @@
50FBA6AC1BA2AFAD00E69BB3 = {
isa = PBXGroup;
children = (
D79F73B724920FBC00B4B883 /* README.md */,
D79F73B624920FBC00B4B883 /* MonkeyKing.podspec */,
D79F73B524920FBC00B4B883 /* Package.swift */,
50FBA6B71BA2AFAD00E69BB3 /* China */,
50FBA6D01BA2BBE900E69BB3 /* MonkeyKing */,
50FBA6B61BA2AFAD00E69BB3 /* Products */,
Expand Down Expand Up @@ -181,6 +189,7 @@
10CDDF3F237A8588005E8DE7 /* MonkeyKing.swift */,
10CDDF45237A8588005E8DE7 /* MonkeyKing+Error.swift */,
07211C7223B8D1AA0068A7C0 /* MonkeyKing+handleOpenURL.swift */,
D71A624324D313E5002B2977 /* MonkeyKing+WeChatUniversalLink.swift */,
07211C7823B8D37E0068A7C0 /* MonkeyKing+Message.swift */,
07211C7A23B8D3A60068A7C0 /* MonkeyKing+OAuth.swift */,
07211C7E23B8D44C0068A7C0 /* MonkeyKing+OpenURL.swift */,
Expand Down Expand Up @@ -352,6 +361,7 @@
07211C7F23B8D44C0068A7C0 /* MonkeyKing+OpenURL.swift in Sources */,
10CDDF4C237A8588005E8DE7 /* MonkeyKing+Error.swift in Sources */,
10CDDF4B237A8588005E8DE7 /* Extensions.swift in Sources */,
D71A624424D313E5002B2977 /* MonkeyKing+WeChatUniversalLink.swift in Sources */,
07211C7923B8D37E0068A7C0 /* MonkeyKing+Message.swift in Sources */,
10CDDF49237A8588005E8DE7 /* AnyActivity.swift in Sources */,
07211C7723B8D2FD0068A7C0 /* MonkeyKing+Pay.swift in Sources */,
Expand Down
9 changes: 8 additions & 1 deletion China/Activities/WeChatActivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ class WeChatActivity: AnyActivity {
}

init(type: Type, message: MonkeyKing.Message, completionHandler: @escaping MonkeyKing.DeliverCompletionHandler) {
MonkeyKing.registerAccount(.weChat(appID: Configs.WeChat.appID, appKey: nil, miniAppID: nil))
MonkeyKing.registerAccount(
.weChat(
appID: Configs.WeChat.appID,
appKey: nil,
miniAppID: nil,
universalLink: Configs.WeChat.universalLink
)
)
super.init(
type: type.activityType,
title: type.title,
Expand Down
13 changes: 9 additions & 4 deletions China/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
if MonkeyKing.handleOpenURL(url) {
return true
}
return false
return MonkeyKing.handleOpenURL(url)
}

func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
return MonkeyKing.handleOpenURL(url)
}

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
return MonkeyKing.handleOpenUserActivity(userActivity)
}
}
1 change: 1 addition & 0 deletions China/Configs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct Configs {
static let appID = "wx4868b35061f87885"
static let appKey = "64020361b8ec4c99936c0e3999a9f249"
static let miniAppID = "gh_d43f693ca31f"
static let universalLink = "YOUR_UNIVERSAL_LINK"
}

struct QQ {
Expand Down
1 change: 1 addition & 0 deletions China/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<string>weibosdk</string>
<string>alipay</string>
<string>alipayshare</string>
<string>weixinULAPI</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
Expand Down
9 changes: 8 additions & 1 deletion China/SystemShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import UIKit
class SystemShareViewController: UIViewController {

@IBAction func systemShare(_ sender: UIButton) {
MonkeyKing.registerAccount(.weChat(appID: Configs.WeChat.appID, appKey: Configs.WeChat.appKey, miniAppID: nil))
MonkeyKing.registerAccount(
.weChat(
appID: Configs.WeChat.appID,
appKey: Configs.WeChat.appKey,
miniAppID: nil,
universalLink: Configs.WeChat.universalLink
)
)
let shareURL = URL(string: "http://www.apple.com/cn/iphone/compare/")!
let info = MonkeyKing.Info(
title: "iPhone Compare",
Expand Down
8 changes: 6 additions & 2 deletions China/WeChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class WeChatViewController: UIViewController {
super.viewDidLoad()

// Should not register account here
let account = MonkeyKing.Account.weChat(appID: Configs.WeChat.appID, appKey: Configs.WeChat.appKey, miniAppID: Configs.WeChat.miniAppID)
let account = MonkeyKing.Account.weChat(appID: Configs.WeChat.appID, appKey: Configs.WeChat.appKey, miniAppID: Configs.WeChat.miniAppID, universalLink: Configs.WeChat.universalLink)
MonkeyKing.registerAccount(account)
}

Expand Down Expand Up @@ -150,7 +150,11 @@ extension WeChatViewController {

@IBAction func OAuthWithoutAppKey(_ sender: UIButton) {
// Should not register account here
let accountWithoutAppKey = MonkeyKing.Account.weChat(appID: Configs.WeChat.appID, appKey: nil, miniAppID: nil)
let accountWithoutAppKey = MonkeyKing.Account.weChat(
appID: Configs.WeChat.appID,
appKey: nil,
miniAppID: nil,
universalLink: Configs.WeChat.universalLink)
MonkeyKing.registerAccount(accountWithoutAppKey)

MonkeyKing.oauth(for: .weChat) { result in
Expand Down
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@

MonkeyKing helps you post SNS messages to Chinese Social Networks, without their buggy SDKs.

MonkeyKing uses the same analysis process of [openshare](https://github.com/100apps/openshare), support share **Text**, **URL**, **Image**, **Audio**, **Video**, and **File** to **WeChat**, **QQ**, **Alipay** or **Weibo**. MonkeyKing can also post messages to Weibo by a webpage. (Note: Audio and Video are specifically for WeChat or QQ, File is only for QQ Dataline)
MonkeyKing uses the same analysis process of [openshare](https://github.com/100apps/openshare).
We also use some reverse engineering tools such as [Hopper Disassembler](https://www.hopperapp.com/) to unveil several undocumented authentication mechanisms under the hood.
It supports sharing **Text**, **URL**, **Image**, **Audio**, **Video**, and **File** to **WeChat**, **QQ**, **Alipay** or **Weibo**.
MonkeyKing can also post messages to Weibo by a web page. (Note: Audio and Video are exclusive to WeChat or QQ, and File is exclusive to QQ Dataline)

MonkeyKing also supports **OAuth** and **Mobile payment** via WeChat and Alipay!

Expand All @@ -34,33 +37,44 @@ Example: Share to WeChat (微信):
1. In your Project Target's `Info.plist`, set `URL Type`, `LSApplicationQueriesSchemes` as follow:

<img src="https://raw.githubusercontent.com/nixzhu/MonkeyKing/master/images/infoList.png" width="600">

You should also add `weixinULAPI` once you enabled Universal Link of your WeChat App.

2. Register account: // it's not necessary to do it here, but for the sake of convenience

```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

MonkeyKing.registerAccount(.weChat(appID: "xxx", appKey: "yyy", miniAppID: nil))

MonkeyKing.regsiterAccount(
.weChat(
appID: "xxx",
appKey: "yyy",
miniAppID: nil,
universalLink: nil // FIXME: You have to adopt Universal Link otherwise your app name becomes "Unauthorized App"(未验证应用)...
)
)
return true
}
```

3. If you need to handle call back, add following code:
3. Append the following code to handle callbacks:

```swift
// AppDelegate.swift

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
//func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { // only for iOS 8

if MonkeyKing.handleOpenURL(url) {
return true
}

return false
return MonkeyKing.handleOpenURL(url)
}
```

Remember to handle userActivities if you are using `UIScene` in your project:
```swift
// SceneDelegate.swift

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
MonkeyKing.handleOpenUserActivity(userActivity)
}
```

to your AppDelegate.

4. Prepare your message and ask MonkeyKing to deliver it:

Expand Down
7 changes: 7 additions & 0 deletions Sources/MonkeyKing/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ extension URL {
}
}

extension URLComponents {

func valueOfQueryItem(_ itemName: String) -> String? {
queryItems?.first(where: { $0.name == itemName })?.value
}
}

extension UIImage {

var monkeyking_compressedImageData: Data? {
Expand Down
2 changes: 1 addition & 1 deletion Sources/MonkeyKing/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ extension MonkeyKing {
var appID = ""
var appKey = ""

for case .weChat(let id, let key, _) in shared.accountSet {
for case .weChat(let id, let key, _, _) in shared.accountSet {
guard let key = key else {
completionHandler(.failure(.noAccount))
return
Expand Down
36 changes: 25 additions & 11 deletions Sources/MonkeyKing/MonkeyKing+Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,7 @@ extension MonkeyKing {
switch message {
case .weChat(let type):
var weChatMessageInfo: [String: Any] = [
"result": "1",
"returnFromApp": "0",
"scene": type.scene,
"sdkver": "1.5",
"command": "1010",
]
let info = type.info
Expand Down Expand Up @@ -239,7 +236,7 @@ extension MonkeyKing {
weChatMessageInfo["objectType"] = "4"
weChatMessageInfo["mediaUrl"] = url.absoluteString
case .miniApp(let url, let path, let withShareTicket, let type):
if case .weChat(_, _, let miniProgramID) = account {
if case .weChat(_, _, let miniProgramID, let universalLink) = account {
weChatMessageInfo["objectType"] = "36"
if let hdThumbnailImage = info.thumbnail {
weChatMessageInfo["hdThumbData"] = hdThumbnailImage.monkeyking_resetSizeOfImageData(maxSize: 127 * 1024)
Expand All @@ -248,6 +245,7 @@ extension MonkeyKing {
weChatMessageInfo["appBrandPath"] = path
weChatMessageInfo["withShareTicket"] = withShareTicket
weChatMessageInfo["miniprogramType"] = type.rawValue
weChatMessageInfo["universalLink"] = universalLink
if let miniProgramID = miniProgramID {
weChatMessageInfo["appBrandUserName"] = miniProgramID
} else {
Expand All @@ -267,17 +265,33 @@ extension MonkeyKing {
} else { // Text Share
weChatMessageInfo["command"] = "1020"
}
var weChatMessage: [String: Any] = [appID: weChatMessageInfo]
if let oldText = UIPasteboard.general.oldText {
weChatMessage["old_text"] = oldText

let wxUrlStr: String

var wxUrlOptions = [UIApplication.OpenExternalURLOptionsKey : Any]()

if
let commandUniversalLink = shared.wechatUniversalLink(of: "sendreq"), #available(iOS 10.0, *),
let universalLink = MonkeyKing.shared.accountSet[.weChat]?.universalLink
{
wxUrlStr = commandUniversalLink
weChatMessageInfo["universalLink"] = universalLink
weChatMessageInfo["isAutoResend"] = false
wxUrlOptions[.universalLinksOnly] = true
} else {
wxUrlStr = "weixin://app/\(appID)/sendreq/?"
}
guard let data = try? PropertyListSerialization.data(fromPropertyList: weChatMessage, format: .binary, options: .init()) else { return }
UIPasteboard.general.setData(data, forPasteboardType: "content")
guard let url = URL(string: "weixin://app/\(appID)/sendreq/?") else {

shared.setPasteboard(of: appID, with: weChatMessageInfo)

guard let url = URL(string: wxUrlStr) else {
completionHandler(.failure(.sdk(.urlEncodeFailed)))
return
}
shared.openURL(url) { flag in

lastMessage = message

shared.openURL(url, options: wxUrlOptions) { flag in
if flag { return }
completionHandler(.failure(.sdk(.invalidURLScheme)))
}
Expand Down
36 changes: 28 additions & 8 deletions Sources/MonkeyKing/MonkeyKing+OAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,46 @@ extension MonkeyKing {
completionHandler(.failure(.sdk(.invalidURLScheme)))
}

case .weChat(let appID, _, _):
case .weChat(let appID, _, _, let universalLink):
let scope = scope ?? "snsapi_userinfo"

if !platform.isAppInstalled {
// SMS OAuth
// uid??
let accessTokenAPI = "https://open.weixin.qq.com/connect/mobilecheck?appid=\(appID)&uid=1926559385"
addWebView(withURLString: accessTokenAPI)
} else {
var urlComponents = URLComponents(string: "weixin://app/\(appID)/auth/")
urlComponents?.queryItems = [
URLQueryItem(name: "scope", value: scope),
URLQueryItem(name: "state", value: "Weixinauth"),
]
var urlComponents: URLComponents?
var wxUrlOptions = [UIApplication.OpenExternalURLOptionsKey : Any]()

if let universalLink = universalLink, let authLink = shared.wechatUniversalLink(of: "auth"), #available(iOS 10.0, *) {
urlComponents = URLComponents(string: authLink)
urlComponents?.queryItems?.append(contentsOf: [
URLQueryItem(name: "scope", value: scope),
URLQueryItem(name: "state", value: "123"), // Weixinauth instead?
])

shared.setPasteboard(of: appID, with: [
"universalLink": universalLink,
"isAuthResend": false,
"command": "0"
])

wxUrlOptions[.universalLinksOnly] = true
} else {
urlComponents = URLComponents(string: "weixin://app/\(appID)/auth/")
urlComponents?.queryItems = [
URLQueryItem(name: "scope", value: scope),
URLQueryItem(name: "state", value: "Weixinauth"),
]
}

guard let url = urlComponents?.url else {
completionHandler(.failure(.sdk(.urlEncodeFailed)))
return
}

shared.openURL(url) { flag in
shared.openURL(url, options: wxUrlOptions) { flag in
if flag { return }
completionHandler(.failure(.sdk(.invalidURLScheme)))
}
Expand Down Expand Up @@ -211,7 +231,7 @@ extension MonkeyKing {
shared.oauthFromWeChatCodeCompletionHandler = completionHandler

switch account {
case .weChat(let appID, _, _):
case .weChat(let appID, _, _, _):
let scope = scope ?? "snsapi_userinfo"

var urlComponents = URLComponents(string: "weixin://app/\(appID)/auth/")
Expand Down
Loading

0 comments on commit aef678e

Please sign in to comment.