如何通过云存储实现iOS端App的扫码安装

需求背景

之前有人咨询,如何通过AppGallery ConnectSDK,实现一个完整的App安装的功能。之前的帖子有描述过主要步骤,但对于不了解AGC的用户,操作起来有点左支右绌。这篇帖子将结合AGC平台的操作,补充介绍实现方法。

由于AppLinking云侧增加了“允许网址清单”设置,清单里不支持URI Scheme格式,因此就不能通过AppLinking进行分享安装链接了。但我们通过原生方法将安装链接转成二维码,还是可以方便的进行扫码安装的。

集成步骤

1、在AppGallery Connect页面创建项目和应用,开通认证服务和云存储服务。开通后,将agconnect-services.plist加入到工程中。

cke_7948.png

2、在认证服务云侧页面,启用匿名帐号。

cke_10917.png

3、云存储的云侧,新增installer文件夹,打开文件夹,上传打好的ipa包。

cke_18008.png

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: 8, bytesPerRow: 0, space: cs, bitmapInfo: 1)!
        
         let context = CIContext.init()
         let bitmapImage = context.createCGImage(image, from: extent)
         bitmap.interpolationQuality = .none
         bitmap.scaleBy(x: scale, y: scale)
         bitmap.draw(bitmapImage!, in: extent)
             
         let scaledImage = bitmap.makeImage()
         return UIImage.init(cgImage: scaledImage!)
     }
}

8、如上述步骤,先生成安装链接,再将链接生成二维码,即可实现App的扫码安装功能。

cke_76412.png

总结

通过InstallerManager产生的链接即安装链接,如果您不想通过扫码安装的方式来安装应用,可自行选择其他方式来安装

相关参考链接:

https://developer.huawei.com/consumer/cn/doc/development/AppGallery-connect-Guides/agc-cloudstorage-getstarted-ios-0000001126781632

欲了解更多更全技术文章,欢迎访问https://developer.huawei.com/consumer/cn/forum/?ha_source=zzh

{{o.name}}
{{m.name}}

猜你喜欢

转载自my.oschina.net/u/4478396/blog/5552964