Quickstart for iOS

πŸ“˜

iOS OS 버전 μ΅œμ†Œ μš”κ΅¬μ‚¬ν•­

LunieWallet SDK λŠ” iOS 15.0 μ΄μƒμ˜ OS 버전을 μ§€μ›ν•©λ‹ˆλ‹€. 15.0 미만의 OS λ²„μ „μ—μ„œλŠ” SDKλ₯Ό ν¬ν•¨ν•œ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ λŸ°νƒ€μž„μ— μ •μƒμ μœΌλ‘œ κ΅¬λ™λ˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. OS 버전에 κ΄€λ ¨ν•œ μžμ„Έν•œ μ •λ³΄λŠ” iOS & IPad OS Release Note νŽ˜μ΄μ§€λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

iOS ν”„λ‘œμ νŠΈμ— Wallet SDK μ—°λ™ν•˜κΈ°

  1. **Project Target > Package Dependencies λ©”λ‰΄λ‘œ μ΄λ™ν•œ λ’€ [+] λ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ λ£¨λ‹ˆ 지갑 SDK μ˜μ‘΄μ„±μ„ μ‹ κ·œλ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.

  1. Nodit Console 에 μ ‘μ†ν•˜μ—¬ κ°€μž…ν•œ λ’€, Wallet SDK Guide νŽ˜μ΄μ§€λ₯Ό μ°Έκ³ ν•˜μ—¬ API Keyλ₯Ό λ°œκΈ‰ λ°›μŠ΅λ‹ˆλ‹€.

  1. 2μ—μ„œ λ°œκΈ‰λ°›μ€ API Key μ‚¬μš©μ„ μœ„ν•΄ configνŒŒμΌμ„ μ‹ κ·œ μƒμ„±ν•œ λ’€ 파일의 λ‚΄μš©μ„ μ•„λž˜μ™€ 같이 μ„€μ •ν•©λ‹ˆλ‹€. λΆ„ν• ν•œ Key Sharesλ₯Ό μ €μž₯ν•  μ„œλ²„μ™€ Wallet API Key에 ν•„μš”ν•œ 값을 μž…λ ₯ν•©λ‹ˆλ‹€.
  • projectFilePath/lunie_wallet_configuration.json
{ 
  "webview_server_url": "${WALLET_WEBVIEW_URL}", // If not entered this field, the Nodit server will be used as the default.
  "api_server_url": "${WALLET_API_URL}", // If not entered this field, the Nodit server will be used as the default.
  "wallet_api_key":"${WALLET_API_KEY}" // required field
}

  1. WebView 톡신 섀정을 μΆ”κ°€ν•©λ‹ˆλ‹€.
  • Project Target > Info λ©”λ‰΄λ‘œ μ΄λ™ν•œ λ’€, App Transport Security Settings ν•­λͺ©κ³Ό Allow Arbitrary Loadsλ₯Ό Yes둜 μ„€μ •ν•©λ‹ˆλ‹€.
<key>App Transport Security Settings </key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

  1. 카메라 섀정을 μΆ”κ°€ν•©λ‹ˆλ‹€.
//카메라섀정좔가
<key>NSCameraUsageDescription</key>
<string>Access camera</string>

//Face ID μ„€μ •μΆ”κ°€ 
<key>Privacy - Face ID Usage Description</key>
<string>Please proceed with the authentication.</string>


SDK μ΄ˆκΈ°ν™” 및 지갑 생성/λ‘œλ“œν•˜κΈ°

  1. LunieWallet SDKλ₯Ό importν•œ ν›„ μ•„λž˜μ™€ 같이 μ„ μ–Έν•˜μ—¬ SDKλ₯Ό μ΄ˆκΈ°ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
import LunieWallet
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        //initialize 
        Lunie.initialize()
        GlobalManager.shared.lunieNetwork = NETWORK_ETHEREUM_GOERLI
        return true
    }
    //...
}

  1. iOS Keychain에 μ €μž₯된 지갑 생성 이λ ₯을 μ‘°νšŒν•œ ν›„, 이λ ₯이 μžˆμ„ 경우 μ €μž₯된 λ‹ˆλͺ¨λ‹‰μ„ 톡해 지갑을 λ‘œλ“œν•˜κ³  이λ ₯이 μ—†λŠ” 경우 μ‹ κ·œ 지갑 Instanceλ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
guard GlobalManager.shared.lunieWallet != nil else {
    return
}
//1. Check Wallet existence
let isAlreadySet = LunieWallet.isAlreadySetWallet()
switch isAlreadySet {
    case .Success(let isAlready):
        if (isAlready) {
      	    // 2. Load saved wallet from SecureLocalStorage
            let lunieInstance = LunieWallet.getInstanceSavedWallet()
            switch lunieInstance {
                case .Success(let data):
                    GlobalManager.shared.lunieWallet = data
                case .Fail(_):
                    GlobalManager.shared.lunieWallet = nil
                    Logger.debug("Fail Load Wallet")
            }
        }
    case .Fail(_):
        GlobalManager.shared.lunieWallet = nil
        Logger.debug("Fail Load Wallet")
}

// Option 2-1. Create New Wallet
var network: LunieNetwork = .init(THE_BALANCE)
let wallet = LunieWallet.getInstanceNewWallet()
switch wallet {
    case .Success(let data):
        GlobalManager.shared.lunieWallet = data
        showAlert("Success Create Wallet")
    case .Fail(_):
        showAlert("WALLET_ALREADY_INITIALIZED")
}

Wallet SDK API

iOS ν™˜κ²½μ—μ„œ 이용 κ°€λŠ₯ν•œ Wallet APIλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

Init For Use

라이브러리 μ΄ˆκΈ°ν™” ν•¨μˆ˜λ‘œ Wallet SDK μ‚¬μš© μ „ ν•„μˆ˜λ‘œ κ°€μž₯ λ¨Όμ € ν˜ΈμΆœλ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

import LunieWallet
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        //initialize Lunie 
        Lunie.initialize()

        //initialize Network 
				GlobalManager.shared.lunieNetwork = .init(THE_BALANCE)
        return true
    }
    //...
}

//GlobalManager.shared.lunieNetwork is initialized as a singleton LunieNetwork? object for use throughout the entire project.
import LunieWallet
class GlobalManager {
    static let shared = GlobalManager()
    var lunieWallet: LunieWallet?
    var lunieNetwork: LunieNetwork?
    var mpcData: SecretSharesOauth?
    var shardKeys: [String] = []
}

Create New Wallet

μƒˆλ‘œμš΄ mnemonic keyλ₯Ό μƒμ„±ν•œ ν›„ 이λ₯Ό μ΄μš©ν•˜μ—¬ Wallet Instanceλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

import LunieWallet

let wallet = LunieWallet.getInstanceNewWallet()
switch wallet {
    case .Success(let data):
        GlobalManager.shared.lunieWallet = data
        print("Success Create Wallet")
    case .Fail(_):
        print("WALLET_ALREADY_INITIALIZED")
}

Clear Wallet

ν˜„μž¬ λ””λ°”μ΄μŠ€μ— μ €μž₯된 Wallet의 Keyλ₯Ό μ‚­μ œν•©λ‹ˆλ‹€. 이λ₯Ό μ΄μš©ν•˜μ—¬ μƒˆλ‘œμš΄ Keyλ₯Ό 생성해 μƒˆλ‘œμš΄ μ£Όμ†Œμ˜ 지갑을 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

let result = LunieWallet.clearWallet()
switch result {
    case .Success(let isclear):
        if (isclear) {
            print("Clear Wallet")
        } else {
            print("Clear Wallet Wrong")
        }
    case .Fail(_):
        print("Clear Wallet Fail")
}

Check Wallet Existence

λ””λ°”μ΄μŠ€μ— μ €μž₯된 지갑이 μ‘΄μž¬ν•˜λŠ”μ§€ μ²΄ν¬ν•œ ν›„ 기기에 μ €μž₯된 mnemonic keyλ₯Ό λΆˆλŸ¬μ™€ Wallet Instanceλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

//1. Check Wallet existence
let isAlreadySet = LunieWallet.isAlreadySetWallet()
switch isAlreadySet {
    case .Success(let isAlready):
        if (isAlready) {
						// 2. Load saved wallet from SecureLocalStorage
            let lunieInstance = LunieWallet.getInstanceSavedWallet()
            switch lunieInstance {
                case .Success(let data):
                    GlobalManager.shared.lunieWallet = data
                case .Fail(_):
                    GlobalManager.shared.lunieWallet = nil
                    print("Fail Load Wallet")
            }
        }
    case .Fail(_):
        GlobalManager.shared.lunieWallet = nil
        print("There are no wallets to read from the device")
}

Load Wallet

ν˜„μž¬ λ””λ°”μ΄μŠ€μ— μ €μž₯된 Wallet Instanceλ₯Ό ν™•μΈν•œ ν›„, Instanceκ°€ μ‘΄μž¬ν•  경우, 이λ₯Ό λΆˆλŸ¬μ˜΅λ‹ˆλ‹€.

let lunieInstance = LunieWallet.getInstanceSavedWallet()
switch lunieInstance {
  case .Success(let data):
    GlobalManager.shared.lunieWallet = data
  case .Fail(_):
    GlobalManager.shared.lunieWallet = nil
    print("Fail Load Wallet")
}

Recovery Wallet(MPC)

지갑을 λ³΅κ΅¬ν•˜λŠ” λ°©λ²•μœΌλ‘œ λ°±μ—…ν•΄ λ‘” SecretSharesλ₯Ό μ΄μš©ν•˜μ—¬ mnemonic을 λ³΅κ΅¬ν•©λ‹ˆλ‹€. 그리고 λ³΅κ΅¬ν•œ mnemonic을 μ΄μš©ν•˜μ—¬ Wallet을 λ³΅κ΅¬ν•©λ‹ˆλ‹€.

// 1. Get secretShares for backup
var secretKeys : [String] = []
let wallet = LunieWallet.getSecretShares()
switch wallet {
    case .Success(let keys):
        secretKeys = keys
    case .Fail(_):
        print("Fail Load SecretKeys")
}

// 2. recovery wallet from secretShares
let lunieInstance = LunieWallet.recoveryWalletFromSecretKeys(secretKeys: secretKeys)
switch lunieInstance {
    case .Success(let wallet):
        GlobalManager.shared.lunieWallet = wallet
    case .Fail(_):
        GlobalManager.shared.lunieWallet = nil
        print("Fail Load Wallet")
}

// 3. Recovery wallet from mnemonic
let mnemonic = ""
let recoveryWallet = LunieWallet.recoveryWalletFromMnemonic(mnemonic: mnemonic)
switch recoveryWallet {
    case .Success(let wallet):
        GlobalManager.shared.lunieWallet = wallet
    case .Fail(_):
        GlobalManager.shared.lunieWallet = nil
        print("Fail Recovery Wallet")
}

Get Address

ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ˜ HD 지갑 μ£Όμ†Œλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.

let result = GlobalManager.shared.lunieNetwork?.getAddress()
switch result {
    case .Success(let address):
        print(address)
    case .Fail(_):
        print("getAddress Fail")
    case .none:
        print("getAddress none")
}

Get NFTs

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή 지갑이 ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ— λ³΄μœ ν•œ NFTλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.

let page = 1 // page >= 1
let itemCount = 10 //  1 <= itemCount <= 100,  Default value is 20
let address = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
GlobalManager.shared.lunieNetwork?.getNFTs(address: address, page: page, rpp: 10) { result in
    switch result {
        case .Success(let nftList):
            print(nftList)
        case .Fail(_):
            print("Fail")
    }
}

Get Native Token

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή 지갑이 ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ— λ³΄μœ ν•œ Native Token을 μ‘°νšŒν•©λ‹ˆλ‹€.

let address = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
GlobalManager.shared.lunieNetwork?.getNativeToken(address:address) { result in
    switch result {
        case .Success(let token):
            print(token)
        case .Fail(_):
            print("Fail")
    }
}

Get Tokens

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή 지갑이 ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ— λ³΄μœ ν•œ ERC20을 μ‘°νšŒν•©λ‹ˆλ‹€.

let address = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
GlobalManager.shared.lunieNetwork?.getTokens(address:address, page: 1, rpp: 10) { result in
    switch result {
        case .Success(let token):
            print(token)
        case .Fail(_):
            print("Fail")
    }
}

Get Gas Limit(NFT)

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή 지갑이 ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ—μ„œ NFTλ₯Ό μ „μ†‘ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ GasLimit을 μ‘°νšŒν•©λ‹ˆλ‹€.

let fromAddress = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
let toAddress = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
let nft = Nft(name: "", contractAddress: "", tokenId: 0, imageUri: "", mediaUri: "", description: "", properties: [], ownerAddress: fromAddress)
GlobalManager.shared.lunieNetwork?.getGasLimit(fromAddress: fromAddress, toAddress: toAddress, nft: nft) { result in
    switch result {
        case .Success(let gasLimit):
            print(gasLimit)
        case .Fail(_):
            print("Fail")
    }
}

Get Gas Limit(Token)

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή 지갑이 ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ—μ„œ Tokenλ₯Ό μ „μ†‘ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ GasLimit을 μ‘°νšŒν•©λ‹ˆλ‹€.

let fromAddress = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
let toAddress = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
let token = Token(name: "", contractAddress: "", symbol: "", decimal: 16, amount: BigInt(0))
GlobalManager.shared.lunieNetwork?.getGasLimit(fromAddress: fromAddress, toAddress: toAddress, token: token, amount: 0) { result in
    switch result {
        case .Success(let gasLimit):
            print(gasLimit)
        case .Fail(_):
            print("Fail")
    }
}

Get Gas Price

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή 지갑이 ν•΄λ‹Ή λ„€νŠΈμ›Œν¬μ—μ„œ Transferλ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ GasPriceλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.

GlobalManager.shared.lunieNetwork?.getGasPrice() { result in
    switch result {
        case .Success(let gasprice):
            print(gasprice)
        case .Fail(_):
            print("Fail")
    }
}

Get Transaction Result By Hash

νŠΈλžœμž­μ…˜μ˜ Hash와 λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή νŠΈλžœμž­μ…˜μ˜ 상세 내역을 μ‘°νšŒν•©λ‹ˆλ‹€.

let hash = "<Transaction Hash>"
  GlobalManager.shared.lunieNetwork?.getTransactionResultByHash(hash: hash) { result in
      switch result {
          case .Success(let transaction):
              print(transaction)
          case .Fail(_):
              print("Fail")
      }
  }

Get Account Transaction Results

νŠΉμ • μ£Όμ†Œλ₯Ό μ΄μš©ν•˜μ—¬ νŠΈλžœμž­μ…˜μ˜ 내역을 μ‘°νšŒν•©λ‹ˆλ‹€.

let address = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
GlobalManager.shared.lunieNetwork?.getAccountTransactionResults(address: address, page: 1, rpp: 10){ result in
    switch result {
        case .Success(let transaction):
            print(transaction)
        case .Fail(_):
            print("Fail")
    }
}

Get Token Transaction From Address

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ ν•΄λ‹Ή μ£Όμ†Œμ˜ ERC20 νŠΈλžœμž­μ…˜μ˜ 상세 내역을 μ‘°νšŒν•©λ‹ˆλ‹€.

let address = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
GlobalManager.shared.lunieNetwork?.getTokenTransactionFromAddress(address: address, page: 1, rpp: 10){ result in
    switch result {
        case .Success(let transaction):
            print(transaction)
        case .Fail(_):
            print("Fail")
    }
}

Transfer NFT

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ μœ μ €μ˜ 지갑이 λ³΄μœ ν•œ NFTλ₯Ό νŠΉμ • μ£Όμ†Œλ‘œ μ „μ†‘ν•˜λŠ” Transferλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.

let toAddress = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
let nft = Nft(name: "", contractAddress: "", tokenId: 0, imageUri: "", mediaUri: "", description: "", properties: [])
GlobalManager.shared.lunieNetwork?.transferNFT(toAddress: toAddress, nft: nft, transferGasFee: nil) { result in
    switch result {
        case .Success(let transaction):
            print(transaction)
        case .Fail(_):
            print("Fail")
    }
}

Transfer Token

νŠΉμ • μ£Όμ†Œμ™€ λ„€νŠΈμ›Œν¬λ₯Ό μ§€μ •ν•˜μ—¬ μœ μ €μ˜ 지갑이 λ³΄μœ ν•œ ERC20을 νŠΉμ • μ£Όμ†Œλ‘œ μ „μ†‘ν•˜λŠ” Transferλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.

let toAddress = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"
let token = Token(name: "", contractAddress: "", symbol: "", decimal: "", amount: BigInt(0))
GlobalManager.shared.lunieNetwork?.transferToken(toAddress: toAddress, token: token, amount: 0, transferGasFee:nil) { result in
    switch result {
        case .Success(let transaction):
            print(transaction)
        case .Fail(_):
            print("Fail")
    }
}

Wallet UI Component Setting

Wallet SDKλŠ” νŽΈλ¦¬ν•œ κ°œλ°œμ„ μ§€μ›ν•˜κΈ° μœ„ν•΄ 기본적인 WebViewλ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. μ•„λž˜ μ½”λ“œλ₯Ό μ΄μš©ν•˜μ—¬ ν•΄λ‹Ή WebViewλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

DispatchQueue.main.async {
		let lunieWebViewController = Lunie.getWebview()
    self.present(lunieWebViewController, animated: true, completion: nil)
}

//OR 

let lunieWebViewController = Lunie.getWebview()
let width = view.bounds.width
let height = view.bounds.height
lunieWebViewController.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
self.view.addSubview(lunieWebViewController.view)