How to add the WeChat API to a Swift project? - ios

I'm trying to add WeChat sharing functionality to my project. However the SDK files, documentation, development guides, and sample project are all in Objective-C. I am writing my project in Swift.
What I've tried
I added the following following SDK files to my project
libWeChatSDK.a
WechatAuthSDK.h
WXApi.h
WXApiObject.h
I tried following the advice on this answer for using a bridging header.
MyProject-Bridging-Header.h
#import "WXApi.h"
Errors
However, I'm still not able to use the WeChat API in my code. I'm getting the following errors:
WXApiObject.h
- (void) setThumbImage:(UIImage *)image; // Expected a type
WXApi.h
+(BOOL) sendAuthReq:(SendAuthReq*) req viewController : (UIViewController*) viewController delegate:(id<WXApiDelegate>) delegate;
// Expected a type
And
<unknown>:0: error: failed to import bridging header '[my path]/MyProject-Bridging-Header.h'
Question
Has anyone been successful in using the WeChat API with a Swift Project? I would love to see a brief list of steps or some sample code. I don't mind if any supplemental links are to Chinese resources.

See #chengsam's answer for newer updates to this process.
How to use the WeChat SDK in your Swift project
The official WeChat developer English instructions seem to be out of date and incomplete. The Chinese instructions are better but only deal with Objective-C. The steps below show how to add the WeChat (weixin 微信) SDK to your project.
Apply for a WeChat App ID
The English site is dev.wechat.com. I was never able to successfully register on the English site, though. I ended up using the Chinese site: open.weixin.qq.com. How to do that is beyond the scope of this answer, but if you don't know Chinese, Google translate can help. Note that you will probably also need a phone number in China.
After your app is approved (you only have to describe your app, no need to provide a binary or source code), you will get an App ID, which you will use in your code.
Download the WeChat SDK
You can try to use the SDK from the English site, but since things seem to be more current at the time of this writing on the Chinese site, that is where I downloaded the SDK for these instructions.
I downloaded the SDK from this page and the link for WeChat SDK version 1.6.2 is here. You will probably want to use the most recent version, though, whatever that may be in the future.
Copy the SDK files into your Xcode project
For organization sake, I put all the files in the same group in my project navigator. (The actual files are still in the project's root folder.)
Bug fix:
In SDK 1.6.2 the issue described in the question appears and is solved by #Anbu.Karthik's answer. Simply add #import <UIKit/UIKit.h> to WXApiObject.h. (You can actually replace #import <Foundation/Foundation.h> with it.) If you are using some SDK version after 1.6.2 this may no longer be an issue, so you may want to just complete the other steps before modifying the SDK files.
Add a Bridging Header
Note that several of the SDK files are Objective-C header (.h) files. In order to use them in your Swift project, you need to add a Bridging Header file to your project. Add a new file (File > New > File... > iOS > Source > Header File ) and call it YourProjectName-Bridging-Header.h.
Add the following line to this file.
#import "WXApi.h"
In my project this is the only line in my bridging header file because it's the only line you need for the WeChat SDK. (There were some other auto generated lines of code when I created the header file, but I just commented them out because I didn't know what they did. I will come back and edit this answer later if I find out they are useful.)
See here, here, and here for more help with adding a bridging header.
There is no need to import any of the WeChat SDK files in your Swift code files now.
Add the required frameworks and libraries
Go to the General tab of your project and scroll down to Linked Frameworks and Libraries. Click the plus (+) button to add the following required frameworks and libraries.
libc++.tbd
CoreTelephony.framework
libsqlite3.tbd
libz.tbd
SystemConfiguration.framework
libWeChatSDK.a (This one should be there already if you have copied it into your project.)
It should now look like this:
The CoreTelephony was mentioned in the readme and the libc++ was mentioned in the online SDK installation instructions but not vice versa. I just added them both to be safe. Feel free to leave a comment if either of these is not actually required.
Add URL Scheme
Go to the Info tab of your project and expand the URL Types item.
Add a type where the identifier is weixin and the URL Schemes is the AppID that you should have gotten after you successfully registered your app with WeChat.
Edit AppDelegate
Edit your AppDelegate.swift file to contain the following functions. Be sure to use your AppID rather than the example one. Other than that you can pretty much copy and paste.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// WeChat: replace with your AppID
WXApi.registerApp("wx68aa08d12b601234")
return true
}
func application(application: UIApplication, handleOpenURL url: NSURL) -> Bool {
return WXApi.handleOpenURL(url, delegate: self)
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return WXApi.handleOpenURL(url, delegate: self)
}
func onReq(req: BaseReq!) {
// do optional stuff
}
func onResp(resp: BaseResp!) {
// do optional stuff
}
// ...
}
See the sample demo app for the optional stuff you can do in onReq and onResp. (The current link is here but if that link is broken, then look for the Sample Demo in the official WeChat developer docs.)
Edit Info.plist
Right click on Info.plist and choose Open As > Source Code. Then add the following two keys before the final </dict>:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
According to the readme included with the SDK, this is necessary because iOS 9 limits http access.
Use the WeChat API
At this point you should be able to start using the WeChat API within your Swift code. I may add a couple more API examples later, but the following is a proof of concept.
Send a text message
let message = SendMessageToWXReq()
message.text = "Hello WeChat"
message.bText = true
message.scene = Int32(WXSceneSession.rawValue) // WXSceneSession
WXApi.sendReq(message)
I was not able to use the Objective-C defined enum WXSceneSession, so I just used its integer value. There is probably a better solution, but this works for now.
This solution above was tested with Xcode 7.2 and iOS 9.2.

Suragch's answer described how to add WeChat API to your app in details. But after my implementation, I figured out the procedure can be simpler now. Below I will highlight some of the changes compared to that answer. You may refer to that answer for more details.
Download the SDK
Using CocoaPods
pod 'WechatOpenSDK'
Manual
Download the SDK from the Resource Page. There are two versions at the time of writing, the first one includes payment function and the second one does not. Choose the one as per your need. After downloading, copy the files to your project. Add the required frameworks and libraries according to that answer. This step is not required if you install using CocoaPods.
Add bridging header
As WXApi is written in Objective-C, we have to create a bridging header to use it in Swift projects. See this link on how to add a bridging header.
After creating the bridging header, insert the following line:
#import "WXApi.h"
iOS 9+ Changes
In AppDelegate, the following two methods are used before iOS 9:
func application(application: UIApplication, handleOpenURL url: NSURL) -> Bool {
return WXApi.handleOpenURL(url, delegate: self)
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return WXApi.handleOpenURL(url, delegate: self)
}
In iOS 9, the above two methods are deprecated and the following one is used:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return WXApi.handleOpen(url, delegate: self)
}
Edit Info.plist
In my case, only the following is needed:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
</array>
If it doesn't work for you, then add the following as well:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Extra
To check if WeChat app is installed on the phone, use the following code:
if WXApi.isWXAppInstalled() {
// do something with WeChat...
} else {
// WeChat app is not installed, show error message
}

Expected a type --> for this error , check once the Apple Documentation for UIImage it's in UIKit, not Foundation. The docs are now all targeted at Swift.
#import UIKit;
In objective-c.
#import <UIKit/UIKit.h>
which you need at the top of your file (no need for the Foundation import either).
failed to import bridging header --> you were made the small Mistake see this link, it helps you

Related

In Swift, how to ignore a part of the code when running from an App Extension target?

There's a similar question that works on Objective-C, but I tried the same code in Swift and it never executes, neither in the main app, nor in the action extension.
My situation is similar to the one in the question above, that is, when running from the main app I want to use UIApplication.shared.open to open a link in Safari, but I want to ignore this part of the code on the App Extension.
The problem isn't finding out whether the app is running from an App Extension or not, but ignoring the code when building for the App Extension, so that the compiler does not give me the following error on build:
You could introduce a new Custom Flag (similar to the DEBUG flag) for the extension target. In your Build Settings look for the Custom Flags and add a new one (e.g. "EXTENSION"). Like here in the screenshot, but also do it for release.
In your Code you could then do something like
#if EXTENSION
// here goes code that is only compiled in the extension
#else
// here goes code that is only compiled outside the extension
#endif
Update: Please read the Apple provided documentation on App Extensions:
Some APIs Are Unavailable to App Extensions
Because of its focused role in the system, an app extension is ineligible to participate in certain activities. An app extension cannot:
Access a Application.shared object, and so cannot use any of the methods on that object
- Apple, App Extension Programming Guide
To programmatically find if the it the running extension via code it's really simple, just do this:
let bundleUrl: URL = Bundle.main.bundleURL
let bundlePathExtension: String = bundleUrl.pathExtension
let isAppex: Bool = bundlePathExtension == "appex"
// `true` when invoked inside the `Extension process`
// `false` when invoked inside the `Main process`

Wrong Symbol Files uploaded by firebase crash reporting

When a crash gets uploaded to the firebase dashboard it always shows the message Upload symbol file to symbolicate future stack traces for UUID *******. However I did make sure that the symbol files got uploaded by looking in the Symbol Files tab. Something I noticed though was that the symbol files had a different UUID then what the message on the crash said. What might I be doing wrong here?
Something strange is that it seems like neither the UUID in uploaded symbol files nor in the message mentioned above is correct. I get a third UUID when I check it up locally by following this guide (basically running find . -iname *.app in ~/Library/Developer/CoreSimulator/Devices/).
I first got this issue in our app using swift 2 and xcode 7. However I also gets the same issue using a new firebase app (ios sdk v3.8) and a minimal xcode 8 project with swift 3 that is setup exactly as advised in the docs.
// AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FIRApp.configure()
return true
}
// ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
FIRCrashMessage("A test message")
fatalError()
}
// Run script in Build Phases that uploads symbol files
GOOGLE_APP_ID=our_google_app_id
"${PODS_ROOT}"/FirebaseCrash/upload-sym "serviceaccount.json"
// Podfile (using latest versions as of 2017-05-12, v3.17.0)
pod 'Firebase/Core'
pod 'Firebase/Crash'
I think maybe the path of "ServiceAccount.json" is wrong, please check again. It should be
"${PODS_ROOT}"/FirebaseCrash/upload-sym "/Path/To/ServiceAccount.json"
And you need also to add this in the script.
rm $HOME/Library/Preferences/com.google.SymbolUpload*

Crashlytics : stuck on "Verifying Installation..." step

I want to use Crashlytics on my application. I followed every step but I'm stuck on "Verifying Installation..." step.
What is the problem ?
setDebugMode = YES
set below code before [Fabric with:#[ TwitterKit ]]; if applied for twitter kit. Or you can simply past below code in didFinishLaunchingWithOptions.
// Swift
Crashlytics().debugMode = true
Fabric.with([Crashlytics.self()])
// Objective-C
[[Crashlytics sharedInstance] setDebugMode:YES];
[Fabric with:#[[Crashlytics class]]];
Now build and run your app and check fabric UI window, it will move ahead to complete screen.
I resolved the problem.
I put my code in applicationDidFinishLaunching(application: UIApplication) instead of func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?)
It seems silly but sometimes you need to make a code change (like a simple NSLog statement) to kick the verification.
If you're adding a new app you need to be an Admin of your organization but you may just be a User.
Just had the same issue on Mac.
In my case the guide shown in the Fabric application was completely missing a crucial step: there was nothing about adding the API key!
So make sure your API key is added to info.plist, otherwise the application gets stuck at verifying installation.
Simply login to Fabric and follow this manual installation guide to make sure everything gets done correctly. Once you complete the last step you can go straight to the dashboard.
I have occurred the same problem. I run the app on iPhone. After I deleted the app and pressed CMD + R on iPhone, the problem is solved.
If you have already installed twitter/Digits kit using fabric before, then you will find Fabric.with([Digits.self, Twitter.self]) in your "AppDelegate.swift" file . Replace it with the following code Fabric.with([Digits.self, Twitter.self, Crashlytics.self]) if you want to use all three. Now press Cmd + R to finish the installation.
Another possible solution is to run the app with connectivity on the device/simulator, as indicated in here.
It could be that the app was already added to your team's account and that all you needed was an access to it. Here's a thread about the same issue: https://twittercommunity.com/t/hmmm-seems-like-your-kit-isnt-activating/73601/9.

iOS8 extension unable to use AFNetworking library

I am unable to use AFNetworking library in my app extension due to
that was trying to access the UIApplication class methods. Does any
one knows here any other better network library that does not access
UIApplication class?. Please suggest.
quote from Guard for unsupported features of iOS 8 extensions #2589
To fix this issue i had to put AF_APP_EXTENSIONS=1 as a preprocessor
macro for the Pods-hitta.se WatchKit Extension-AFNetworking target.
Adding #define AF_APP_EXTENSIONS to the prefix header didn't work,
neither did adding it as a preprocessor macro to the watch extension
target work.
So click on Pods project in Project Navigator, then select Pods-{your-app-name-extension}-AFNetworking. Next go to Build Settings and find Preproccessor Macros where you add new line with "AF_APP_EXTENSIONS=1"
Also wrap it with +#if !defined(AF_APP_EXTENSIONS) at beginning. And end it +#endif
full example of wrapping:
- (void)updateNetworkActivityIndicatorVisibility {
+#if !defined(AF_APP_EXTENSIONS)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]];
+#endif
}
You can use AFNetworking with extension. You just need follow the instruction below.
When using AFNetworking in an App Extension, #define AF_APP_EXTENSIONS
to avoid using unavailable APIs.
https://github.com/AFNetworking/AFNetworking/issues/2119

how to integrate flurry in ios using swift language

I want to integrate flurry in ios using swift language.
I added the flurry sdk's two files : flurrylib.a and flurry.h and then entered my api key in project TestProject4-Bridging-Header.h
#import "Flurry.h"
I had the same issue and adding a bridging header worked for me. Here are the steps I followed.
Note: If you already have a bridging file, skip the steps 2 to 4
I dragged the Flurry folder to my Swift iOS project in XCode.
I created a bridging header file by creating a new Objective-C header file (in XCode menu it's File/New/File/iOS/Source/Header File)
I called the file name "PROJECTNAME-Bridging-Header.h" (replace PROJECTNAME with your XCode project name)
In Project Build Settings, I searched for "Swift Compile - Code Generation" and there in "Objective-C Bridging Header" I entered the name of my new bridging file (PROJECTNAME-Bridging-Header.h) for debug and release values.
Now, back in my bridging header file, I referenced the Flurry header file.
#import "Flurry.h"
In my AppDelegate.swift file, I could then call the Flurry methods using Swift:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// setup Flurry
Flurry.startSession(flurryKey) // replace flurryKey with your own key
Flurry.setCrashReportingEnabled(true) // records app crashing in Flurry
Flurry.logEvent("Start Application") // Example of even logging
Hope it helps someone.
Flurry Integration using Pods in Swift
You need to import Flurry SDK as Follows
import Flurry_iOS_SDK
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Flurry.startSession("YOUR_API_KEY")
Flurry.logEvent("Started Application")
}
If you are attempting to log events in the 'didFinishLaunchingWithOptions' using Flurry SDK 6.0.0, this is not possible due to a bug in that version of the SDK. It is possible to start a session from this function however. I was told this information by a Flurry support rep, and confirmed it in my own testing.
You need to add a bridging header to your project. In that bridging header you import what you need in Swift and then you don't need to import it anywhere else.
The iBook on using Swift and Obj-C alongside has a very good explanation on this.
Actually, there is an explanation on Apple's Swift blog that says:
To be safe, all components of your app should be built with the same version of Xcode and the Swift compiler to ensure that they work together.
You can find more information here.

Resources