如何通过云存储实现iOS端App的扫码安装
需求背景
之前有人咨询,如何通过AppGallery Connect的SDK,实现一个完整的App安装的功能。之前的帖子有描述过主要步骤,但对于不了解AGC的用户,操作起来有点左支右绌。这篇帖子将结合AGC平台的操作,补充介绍实现方法。
由于AppLinking云侧增加了“允许网址清单”设置,清单里不支持URI Scheme格式,因此就不能通过AppLinking进行分享安装链接了。但我们通过原生方法将安装链接转成二维码,还是可以方便的进行扫码安装的。
集成步骤
1、在AppGallery Connect页面创建项目和应用,开通认证服务和云存储服务。开通后,将agconnect-services.plist加入到工程中。
2、在认证服务云侧页面,启用匿名帐号。
3、云存储的云侧,新增installer文件夹,打开文件夹,上传打好的ipa包。
4、在工程中,如下编辑Podfile文件。
target 'AGC-Installer' do
use_frameworks!
pod 'AGConnectAuth'
pod 'AGConnectStorage'
end
5、在AppDelegate里初始化AGCInstance实例。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
AGCInstance.startUp()
return true
}
6、如下示例代码编辑InstallerManager.swift文件。
import UIKit
import AGConnectStorage
let installerDir = "installer/"
let storage: AGCStorage = AGCStorage.getInstanceForBucketName("mystorage-awfpi")
let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as NSString
let infoDictionary = Bundle.main.infoDictionary!
let bundleName = infoDictionary["CFBundleName"] as! String
let bundleVersion = infoDictionary["CFBundleShortVersionString"] as! String
class InstallerManager: NSObject {
func ipaDownloadUrl(ipaUrlBlock: @escaping (String) -> Void) {
let storageReference = storage.reference(withPath: installerDir + bundleName + ".ipa")
storageReference.getDownloadUrl().onSuccess(callback: { (url) in
ipaUrlBlock(url!.absoluteString!)
}).onFailure(callback: { (error) in
})
}
func writeToPlist(writeBlock: @escaping (Bool) -> Void) {
self.ipaDownloadUrl { (str) in
print(str)
let urlItem = NSMutableDictionary()
urlItem.setValue("software-package", forKey: "kind")
urlItem.setValue(str, forKey: "url")
let assets = NSArray(object: urlItem)
let metadata = NSMutableDictionary()
metadata.setValue(Bundle.main.bundleIdentifier, forKey: "bundle-identifier")
metadata.setValue(bundleVersion, forKey: "bundle-version")
metadata.setValue("software", forKey: "kind")
metadata.setValue(bundleName, forKey: "subtitle")
metadata.setValue(bundleName, forKey: "title")
let item0 = NSMutableDictionary()
item0.setValue(assets, forKey: "assets")
item0.setValue(metadata, forKey: "metadata")
let items = NSArray(object: item0)
let plist = NSMutableDictionary()
plist.setValue(items, forKey: "items")
let path = documentPath.appendingPathComponent("App.plist")
let result = plist.write(toFile: path, atomically: true)
writeBlock(result)
}
}
func uploadPlist(uploadPlistBlock: @escaping (AGCStorageReference) -> Void) {
let storageReference = storage.reference(withPath: installerDir + "App.plist")
let task = storageReference.uploadFile(URL(fileURLWithPath: documentPath.appendingPathComponent("App.plist")))
task?.onSuccess(callback: { (result) in
print("upload success")
uploadPlistBlock(storageReference)
}).onFailure(callback: { (error) in
print("upload faile")
})
}
func installerUrl(installerUrlBlock: @escaping (String) -> Void) {
self.writeToPlist { (writed) in
if writed {
self.uploadPlist { (storageReference) in
storageReference.getDownloadUrl().onSuccess(callback: { (url) in
let urlStr = url!.absoluteString! as NSString
let customAllowedSet = NSCharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]").inverted
let encodeurl = urlStr.addingPercentEncoding(withAllowedCharacters: customAllowedSet)
let downloadUrl = "itms-services:///?action=download-manifest&url=" + encodeurl!
installerUrlBlock(downloadUrl)
}).onFailure(callback: { (error) in
assert(false, "can not get ipa installer url")
})
}
}
}
}
}
7、如下编辑ViewController.swift文件。
import UIKit
import AGConnectStorage
import AGConnectAuth
class ViewController: UIViewController {
@IBOutlet weak var QRCodeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if AGCAuth.instance().currentUser == nil {
AGCAuth.instance().signInAnonymously().onSuccess { (result) in
print("login success")
}.onFailure { (error) in
print("login fail")
}
}
}
@IBAction func getInstallerLink(_ sender: Any, forEvent event: UIEvent) {
let installer = InstallerManager()
installer.installerUrl { (str) in
let mydefault = UserDefaults.standard
mydefault.set(str, forKey: "link")
let alert = UIAlertController.init(title: "link", message: str, preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
@IBAction func getQRCode(_ sender: Any, forEvent event: UIEvent) {
let filter = CIFilter.init(name: "CIQRCodeGenerator")
filter?.setDefaults()
let mydefault = UserDefaults.standard
let link = mydefault.value(forKey: "link") as? String
guard link != nil else {
let alert = UIAlertController.init(title: "", message: "generate link first", preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
let data = link!.data(using: .utf8)
filter?.setValue(data, forKeyPath: "inputMessage")
let outputImage = filter?.outputImage
QRCodeImageView.image = self.createUIImageFromCIImage(image: outputImage!, size: 50)
}
func createUIImageFromCIImage(image: CIImage, size: CGFloat) -> UIImage {
let extent = image.extent.integral
let scale = min(size / extent.width, size / extent.height)
let width: size_t = size_t(extent.width * scale)
let height: size_t = size_t(extent.height * scale)
let cs: CGColorSpace = CGColorSpaceCreateDeviceGray()
let bitmap: CGContext = CGContext(data: nil, width: width, height: height, bitsPerComponent: <span class="hljs-number">8</span>, bytesPerRow: <span class="hljs-number">0</span>, space: cs, bitmapInfo: <span class="hljs-number">1</span>)!
<span class="hljs-keyword">let</span> context = CIContext.init()
<span class="hljs-keyword">let</span> bitmapImage = context.createCGImage(image, from: extent)
bitmap.interpolationQuality = .none
bitmap.scaleBy(x: scale, y: scale)
bitmap.draw(bitmapImage!, <span class="hljs-keyword">in</span>: extent)
<span class="hljs-keyword">let</span> scaledImage = bitmap.makeImage()
<span class="hljs-keyword">return</span> UIImage.init(cgImage: scaledImage!)
}
}</code></pre>
<img class="cke_reset cke_widget_mask" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==">
8、如上述步骤,先生成安装链接,再将链接生成二维码,即可实现App的扫码安装功能。
总结
通过InstallerManager产生的链接即安装链接,如果您不想通过扫码安装的方式来安装应用,可自行选择其他方式来安装。
相关参考链接:
欲了解更多更全技术文章,欢迎访问https://developer.huawei.com/consumer/cn/forum/?ha_source=zzh