반응형

이전 포스팅

react-native-iap 가이드(1) - 설치 및 설정

https://honeystorage.tistory.com/331

 

 

가이드 2 - 상품 생성

코드를 작성하기에 앞서

상품을 각 스토어에 생성해주어야한다.

작업은 매우 쉽다.

 

다만, 동일 상품이라면

각 스토어에 아이디를 동일하게 생성해주자.

이렇게 하면 관리 측면에서 더 유리할 것으로 보인다.

 

 

1. IOS

 

앱 내 구입으로 들어가 보면

쉽게 상품을 추가할 수 있다.

소모품 / 비소모품 / 자동 갱신 구독 / 비자동 갱신 구독

4가지 유형의 상품이 존재한다.

 

원하는 상품을 생성하면 되며

생성할 때에는 검수 통과를 위해

상품 관련 내용을 매우 상세하게 작성해야한다.

(상품명, 가입 기간, 가입 기간동안 제공되는 혜택 및 서비스, 가격 (혹은 단위당 가격), 청구 방식, 자동 구독 방식인 경우 최소 24시간전에 취소 하지 않으면 자동 구독 결제됨을 표기, 구독 해지 방법 안내, 구매 관련 개인보호 방책 링크 등)

 

검수는 한글로 넣어도 되지만

한글을 기본으로 줄바꿈하여
번역기를 돌린 영문도 넣어주면 금상첨화

 

 

2. android

안드로이드도 크게 다르지 않다.

인앱상품 / 정기 결제에 상품을 추가해주면 된다.

ios와 달리 검수를 위한 정보를 제공할 필요도 없다.

 

 

3. 플랫폼별 ID, 앱에 정의

각 플랫폼별로 상품을 다 추가했다면

다음 포스팅의 가이드를 따라서

각 플랫폼별 상품을 skus/subs로 나누어 추가해보도락 하자.

 

다음 포스팅

react-native-iap (3) - 코드 작성

https://honeystorage.tistory.com/332

 

 

 

 

반응형
반응형

이전 포스팅

react-native-iap 가이드(1) - 설치 및 설정

https://honeystorage.tistory.com/331

 

가이드 3 - 코드작성

군말 없이 바로 코드 작성에 들어가보자

hooks 형태로 작성했으며

코드는 깔끔하지 않다

각자 입맛에 맞게 깔끔하게 정리해서 쓰도록 해보자.

 

먼저, 구현할 코드는 아래와 같다.

1. 스토어 커넥션 connection initialize

2. 리스너 붙이기 attach listener

3. 커넥션 해지 및 리스너 해지 end connection & detach listener

4. 아이템 불러오기 get products from play store / app store

5. 결제하기 payment

6. 구매 복원 restore 

 

 

1. 스토어 커넥션 connection initialize

안드로이드 개발 환경에서는 에러를 겪을 수 있다.

Not initialized ... unreachable... billing ...

별의 별 에러가 계속해서 나타나지만 무시하자.

여기서 몇 일의 시간을 허비했지만  배포 버전에는 문제 없습니다.

 

import RNIap from 'react-native-iap';
import { Platform } from 'react-native';

function useShoppingState() {
  useEffect(() => {
    const connection = async () => {
      try {
        const init = await RNIap.initConnection();
        const initCompleted = init === true;
        
        if (initCompleted) {
          if (Platform.OS === 'android') {
            await RNIap.flushFailedPurchasesCachedAsPendingAndroid();
          } else {
            await RNIap.clearTransactionIOS();
          }
        }
        
      } catch(error) {
        console.log('connection error: ', error);
      }
    }
    
    connection();
  }, [])
}

기본 연결을 완료하였다.

 

 

2, 3. 리스너 붙이기 +  커넥션 해지 및 리스너 해지

결제 요청후 리스폰스를 받을 리스너를 등록해야하는데

가이드에서는 리스너를 connection과 함께

useEffect (혹은 componentDidmount)에 작성할것을 권장한다.

import RNIap, { InAppPurchase, PurchaseError, finishTransaction } from 'react-native-iap';
import { Platform, Alert } from 'react-native';

function useShoppingState() {
  let purchaseUpdateSubscription: any;
  let purchaseErrorSubscription: any;
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    const connection = async () => {
      try {
        const init = await RNIap.initConnection();
        const initCompleted = init === true;
        
        if (initCompleted) {
          if (Platform.OS === 'android') {
            await RNIap.flushFailedPurchasesCachedAsPendingAndroid();
          } else {
            await RNIap.clearTransactionIOS();
          }
        }
        
        // success listener
        purchaseUpdateSubscription = purchaseUpdatedListener(
          async (purchase: InAppPurchase | SubscriptionPurchase) => {
            const receipt = purchase.transactionReceipt ? purchase.transactionReceipt : purchase.purchaseToken;
            
            if (receipt) {
              try {
                setLoading(false);
                const ackResult = await finishTransaction(purchase);
                
                // 구매이력 저장 및 상태 갱신
                if (purchase) {
                  
                }
              } catch(error) {
                console.log('ackError: ', error);
              }
            }
          }
        );
        
        purchaseErrorSubscription = purchaseErrorListener((error: PurchaseError) => {
          setLoading(false);
         
          // 정상적인 에러상황 대응
          const USER_CANCEL = 'E_USER_CANCELED';
          if (error && error.code === USER_CANCEL) {
            Alert.alert('구매 취소', '구매를 취소하셨습니다.');
          } else {
            Alert.alert('구매 실패', '구매 중 오류가 발생하였습니다.');
          }
        });
      } catch(error) {
        console.log('connection error: ', error);
      }
    }
    
    connection();
    
    return () => {
      if (purchaseUpdateSubscription) {
        purchaseUpdateSubscription.remove();
        purchaseUpdateSubscription = null;
      }
      
      if (purchaseErrorSubscription) {
        purchaseErrorSubscription.remove();
        purchaseErrorSubscription = null;
      }
      
      RNIap.endConnection();
    }
  }, [])
}

정상적인 구매 완료가 확인되면

storage에 상태를 저장하거나

서버에 저장하는 등

각자 상황에 맞는 다음 스텝을 밟으면 된다.

 

그리고 구매 완료를 확실하게 확인하려면

RNIap의 getAvailablePurchases 메소드를 통해

확인 가능하다.

 

 

4. 아이템 불러오기

결제할 상품의 ID를 이미 알고있지만

상품을 각 스토어에서 불러오는 작업을 해주어야한다.

 

각 스토어에서 실제로 운영중인 상품에만

결제를 진행해주기위한 작업인 것으로 보인다.

 

이를 반증하듯, ID를 통해 요청했더라도

현재 운영중이지 않은 상품은 response 받을 수 없다.

response 받은 상품들의 정보를 가지고

화면에 상품 목록을 만들어 주면 된다.

 

const itemSkus: any = Platform.select({
  ios: [
    ios_item1_id,
    ios_item2_id
  ],
  android: [
    aos_item1_id,
    aos_item2_id
  ]
});

const itemSubs: any = Platform.select({
  ios: [
    ios_monthly_subs_id,
    ios_yearly_subs_id
  ],
  android: [
    aos_monthly_subs_id,
    aos_yearly_subs_id
  ]
});

function useShoppingState() {
  ...
  
  const getItems = async () => {
    try {
      const items = await RNIap.getProducts(itemSkus);
      // items 저장
      ...
    } catch(error) {
      console.log('get item error: ', error);
    }
  }
  
  const getSubscriptions = async () => {
    try {
      const subscriptions = await RNIap.getSubscriptions(itemSubs);
      // subscriptions 저장
      ...
    } catch(error) {
      console.log('get subscriptions error: ', error);
    }  
  }
}

기존의 코드에 코드를 추가했다.

itemSubs, itemSkus와 같이

각 플랫폼별 상품 ID 목록을 정의해주어야한다.

 

그렇게 받아온 상품 목록을 통해 shopping list를

사용자에게 제공하면 된다.

 

상품 정보에는

상품명, 상품 ID, 가격, 로컬라이징 가격, 상품 유형 등이 포함된다.

 

 

5. 결제하기

결제 요청도 상품 유형에 맞게 요청해야 한다.

서버로부터 받은 상품 정보에 상품 유형이 포함되어 있으니

해당 정보르르 이용해 알맞게 요청해보자.

 

function useShoppingState() {
  ...
  
  const requestItemPurchase = async (sku: string) => {
    try {
      RNIap.requestPurchase(sku);
    } catch(error) {
      console.log('request purchase error: ', error);
      Alert.alert(error.message);
    }
  }
  
  const requestSubscriptionPurchase = async (sub: string) => {
    try {
      RNIap.requestPurchase(sub);
    } catch(error) {
      console.log('request purchase error: ', error);
      Alert.alert(error.message);
    }  
  }
  
  return { requestItemPurchase, requestSubscriptionPurchase }
}

 

상품 목록을 구현한 뒤

상품 터치에 따른 구매 코드를 작성한다면

아래와 같이 상품 유형별로 나누어 작성하면 된다.

// 구매
const onPurchase = (item) => {
  if (item.type === 'subs') {
    requestSubscriptionPurchase(item.productId);
  } else {
    requestItemPurchase(item.productId);
  }
}

 

 

6. 구매 복원

애플에서 필수적으로 지원할것을

권장하는 기능인 구매복원.

 

예를들어,

1개월 구독 상품을 어제 구매했던 사용자가

이용한지 3일 정도 지난 상태에서

앱을 삭제한뒤 다시 받았을 때

구독 상품을 정상적으로 다시 이용할수 있도록

돕기 위한 기능이다.

 

위에서 언급했던

getAvaliablePurchases 메소드를 통해

구매 이력을 확인할 수 있다.

 

받아온 정보를 통해

구매했던 상품이 아직 유효한지 검증한뒤

유효 하다면 구매복원 처리 해주면 된다.

 

 

 

// Full code

import RNIap, { InAppPurchase, PurchaseError, finishTransaction } from 'react-native-iap';
import { Platform, Alert } from 'react-native';

const itemSkus: any = Platform.select({
  ios: [
    ios_item1_id,
    ios_item2_id
  ],
  android: [
    aos_item1_id,
    aos_item2_id
  ]
});

const itemSubs: any = Platform.select({
  ios: [
    ios_monthly_subs_id,
    ios_yearly_subs_id
  ],
  android: [
    aos_monthly_subs_id,
    aos_yearly_subs_id
  ]
});

function useShoppingState() {
  let purchaseUpdateSubscription: any;
  let purchaseErrorSubscription: any;
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    const connection = async () => {
      try {
        const init = await RNIap.initConnection();
        const initCompleted = init === true;
        
        if (initCompleted) {
          if (Platform.OS === 'android') {
            await RNIap.flushFailedPurchasesCachedAsPendingAndroid();
          } else {
            await RNIap.clearTransactionIOS();
          }
        }
        
        // success listener
        purchaseUpdateSubscription = purchaseUpdatedListener(
          async (purchase: InAppPurchase | SubscriptionPurchase) => {
            const receipt = purchase.transactionReceipt ? purchase.transactionReceipt : purchase.purchaseToken;
            
            if (receipt) {
              try {
                setLoading(false);
                const ackResult = await finishTransaction(purchase);
                
                // 구매이력 저장 및 상태 갱신
                if (purchase) {
                  
                }
              } catch(error) {
                console.log('ackError: ', error);
              }
            }
          }
        );
        
        purchaseErrorSubscription = purchaseErrorListener((error: PurchaseError) => {
          setLoading(false);
         
          // 정상적인 에러상황 대응
          const USER_CANCEL = 'E_USER_CANCELED';
          if (error && error.code === USER_CANCEL) {
            Alert.alert('구매 취소', '구매를 취소하셨습니다.');
          } else {
            Alert.alert('구매 실패', '구매 중 오류가 발생하였습니다.');
          }
        });
        
        getItems();
        getSubscriptions();
      } catch(error) {
        console.log('connection error: ', error);
      }
    }
    
    connection();
    
    return () => {
      if (purchaseUpdateSubscription) {
        purchaseUpdateSubscription.remove();
        purchaseUpdateSubscription = null;
      }
      
      if (purchaseErrorSubscription) {
        purchaseErrorSubscription.remove();
        purchaseErrorSubscription = null;
      }
      
      RNIap.endConnection();
    }
  }, [])
  
  const getItems = async () => {
    try {
      const items = await RNIap.getProducts(itemSkus);
      // items 저장
      ...
    } catch(error) {
      console.log('get item error: ', error);
    }
  }
  
  const getSubscriptions = async () => {
    try {
      const subscriptions = await RNIap.getSubscriptions(itemSubs);
      // subscriptions 저장
      ...
    } catch(error) {
      console.log('get subscriptions error: ', error);
    }  
  }
  
  const requestItemPurchase = async (sku: string) => {
    try {
      RNIap.requestPurchase(sku);
    } catch(error) {
      console.log('request purchase error: ', error);
      Alert.alert(error.message);
    }
  }
  
  const requestSubscriptionPurchase = async (sub: string) => {
    try {
      RNIap.requestPurchase(sub);
    } catch(error) {
      console.log('request purchase error: ', error);
      Alert.alert(error.message);
    }  
  }
  
  return { requestItemPurchase, requestSubscriptionPurchase }
}
반응형
반응형

react-native 결제 생태계에

dooboolab 한국인 개발자 팀이 큰 기여를 했다.

 

적용에 다소 난항이 있었지만

라이브러리 소스코드 수정없이

출시된 버전을 통해

정기결제, 인앱상품 결제 구현을 완료했다.

 

dooboolab팀에 감사인사를 드리며

8.0.4 버전에 맞춰 가이드를 작성해본다.

 


가이드 1 - 설치 및 설정

yanr, npm 둘중 무엇을 사용해도 문제는 없다.

다만, 반드시 최신에 나온 안정화 버전인 8.0.4를 설치하라

(안그러면 머리카락 1000가닥쯤 빠짐)

 

yarn add react-native-iap@8.0.4
or
npm install react-native-iap@8.0.4

 

pod 설치도 마저 해주자

cd ios && pod install && cd ..

 

 xcode에서 in-app purchase도 추가해주어야 한다.

 

android에도 약간의 설정이 필요하다.

안드로이드의 설정은

google play 결제만 이용하느냐

amazon 결제도 이용하느냐에 따라 조금 바뀐다.

아래의 링크를 참고하되

google play 결제만 이용한다면 나의 가이드를 따르자

// android/build.gradle

buildscript {
  ext {
    buildToolsVersion = "30.0.2"
    minSdkVersion = 21
    compileSdkVersion = 30
    targetSdkVersion = 30
    ndkVersion = "21.4.7075529"
    
    androidXAnnotation = "1.1.0"
    androidXBrowser = "1.0.0"
  }
  
  ...
}

나는 targetSdkVersion을 29 -> 30으로 변경

androidXAnnotation, androidXBrowser 부분을

추가 해주었던 것으로 기억한다.

https://react-native-iap.dooboolab.com/docs/installing

 

// android/app/build.gradle

androd {
  ...
  
  defaultConfig {
    ...
    missingDimensionStrategy 'store', 'play'
  }
}
// androidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.vending.BILLING"/>

설치 및 설정은 여기까지.

 

 

추가로

npm install react-native-iap을 통해

설치된 기본 버전에서는 에러가 발생했었다.

 

issue 트랙킹 결과 7.5.1로 넘어가게 됐고

거기서도 에러가 발생해

8.0.4까지 오게되었으니

 

반드시 처음부터 8.0.4를 받아서

머리카락 손실을 줄이자

 

 

 

다음 포스팅

react-native-iap (2) - 상품 생성

https://honeystorage.tistory.com/334

 

react-native-iap (3) - 코드 작성

https://honeystorage.tistory.com/332

반응형
반응형
text-decoration: underline solid #8e8e93;
반응형
반응형

1. 설치

// android sdk version > 30
yarn add @react-native-admob/admob

// android sdk version > 30
yarn add @react-native-admob/admob@1.5.1

cd ios && pod install && cd ..

 

 

2. App ad id 설정

// info.plist
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-YYYYYYY~YYYYYYY</string>
<key>SKAdNetworkItems</key>
<array>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>cstr6suwn9.skadnetwork</string>
  </dict>
</array>


// android/app/src/main/Anroidmeniest.xml
<application>
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-YYYYYYY~YYYYYY"/>
</application>

 

 

3. app-ads.txt

구글 애드몹 사이트에 로그인해서 앱을 등록을 먼저 해야한다.

앱이 이미 출시되어있다면 정말 쉽게 등록 가능하다.

(출시 안되어있을떄는 어떤지는 모르겠다.)

 

앱을 모두 등록한뒤

좌측 메뉴 목록 중에서 "앱 > 모든 앱 보기" 를 선택한다.

 

그러면 app-ads.txt 탭을 볼 수 있다.

일단 2번 항목을 복사하고 닫자.

 

구글 공식 크롤링 가이드에 따르면

도메인 단위로 app-ads.txt를 수집한다고 한다.

 

예를들어,

앱 스토어/플레이 스토어에 지원 페이지 링크가

www.help.com/service  라면

www.help.com/app-ads.txt  을 크롤링 하여

등록 여부를 체크하는것이다.

 

따라서, 우리는 해당 링크에 접근했을 때

app-ads.txt가 보여지게 설정해줘야 한다.

(각자 환경에 맞게)

 

app-ads.txt의 내용은

위에 2번 항목을 복사한 내용이면 충분하다.

 

 

4. 앱 정상 실행 확인하기

라이브러리를 설치하고나면

앱이 정상적으로 잘 실행되는지

확인 할 필요가 있습니다.

 

각 os에서 앱을 실행해보고

에러가 난다면

npm start --reset-cache

를 통해 캐시를 한번

정리해주는것을 추천드립니다.

 

 

5. 테스트 코드로 광고 띄워보기

function AdTest() {
  const TEST_FULLPAGE_VIDEO_AD = 'ca_~~~';
  const { adLoaded, adDismissed, show, load } = useInterstitialAd(TEST_FULLPAGE_VIDEO_AD)
  
  const showAd = () => {
    const isNotShownAd = adLoaded && !adDismissed;
    if (isNotShownAd) {
      show();
    }
  }
  
  return {
    <>
      <TouchalbeOpacity onPress={showAd}>
        <Text>광고 짠</Text>
      </TouchableOpacity>
    </>
  }
}

광고를 보여주고나면

adDisimissed가 true 상태로 전환되는데

load 메소드를 통해

광고를 리필(?)해 줄 수 있습니다.

리필 후에는 adDismissed가 다시 false로 전환되며

필요에따라 다시 광고를 띄워줄 수 있습니다

반응형
반응형

옵션에 vertical을 부여했을때

위와 같은 에러가 발생함을 발견했다.

 

크기 지정에 있어 에러가 발생하는듯 했고

Carousel의 Props을 확인해보니

Vertical 속성이 부여됐을때

sliderHeight와 itemHeight가 필수 옵션임을 확인할 수 있었다.

 

두 옵션을 임의로 지정해준 결과

에러가 해결됐음을 확인할 수 있었다.

 

에러가 발생했을때는 마땅한 이유가 있다.

특히, 라이브러리 사용에 있어 에러가 발생한 경우에는

제작자의 의도대로 사용하지 않았을 확률이 제법 있다.

 

에러 로그를 정확히 확인하고

분석하는 노력을 해보자.

반응형
반응형

글로벌 서비스를 개발할때

기본 언어를 해당 사용자에 맞게

설정해주는 것은

사용자를 위한 최소한의 배려이다

 

간단하게

react-native 앱의 기본언어

설정 방법에 대해 알아본다.

 

[ 목차 ]

1. 디바이스 OS별 언어 설정 불러오기

2. 기본 언어 설정해주기

3. 전체 상태 변경

 

import {NativeModules, Platform} from 'react-native';

const useLanguageState = () => {
  useLayoutEffect(() => {
    const language = getSystemLanguage();
    console.log(language);
  }, []);
  
  const getSystemOriginLanguage = () => {
    let systemLanguage = '';
    if (Platform.OS === 'ios') {
      systemLanguage = NativeModules.SettingsManager.settings.AppleLocale
      || NativeModules.SettingsManager.settings.AppleLanguages[0];
    } else {
      NativeModules.I18nManager.localeIdentifier;
    }
    
    const language = systemLanguage.toLowerCase().indexOf('ko') >= 0 ? 'KR' : 'EN';
    return language;
  }
}

OS별로 시스템 랭귀지리를 불러와

콘솔에 찍어 확인해보았다.

 

기본 언어를 설정해주어야 하는데

앱이 재실행 됐을때도 해당 언어로

상태를 유지해주기위해

스토리지에 저장해 주도록 한다.

 

import {NativeModules, Platform} from 'react-native';
import storage from '@react-native-async-storage/async-storage';

type LanguageTypes = 'KR' | 'EN';

const useLanguageState = () => {
  useLayoutEffect(() => {
    const language = getSystemLanguage();
    setupUserLanguage(language);
  }, []);
  
  const getSystemOriginLanguage = () => {
    let systemLanguage = '';
    if (Platform.OS === 'ios') {
      systemLanguage = NativeModules.SettingsManager.settings.AppleLocale
      || NativeModules.SettingsManager.settings.AppleLanguages[0];
    } else {
      NativeModules.I18nManager.localeIdentifier;
    }
    
    const language = systemLanguage.toLowerCase().indexOf('ko') >= 0 ? 'KR' : 'EN';
    return language;
  }
  
  const setupUserLanguage = async (systemLanguage: LanguageTypes) => {
    const savedLanguage = await storage.getItem('language');
    if (!savedLanguage) {
      await updateUserDefaultLanguage(systemLanguage);
    }
  }
  
  const updateUserDefaultLanguage = async (lang: LanguageTypes) => {
    await storage.setItem('language', lang);
  } 
}

storage에 이전에 저장된 값이 있으면

갱신을 생략하고 없으면 갱신하는 구조이다.

 

이제 변경된 상태를

global state관리 도구로

서비스 전체에 적용해주면 되는데

여기서는 react-redux를 사용한다.

 

 

import {NativeModules, Platform} from 'react-native';
import {useDispatch, useSelector} from 'react-redux';
import storage from '@react-native-async-storage/async-storage';

import {RootState} from '@redux/store';
import {changeGlobalLanguage} from '@redux/setup/actions';

type LanguageTypes = 'KR' | 'EN';

const useLanguageState = () => {
  const dispatch = useDispatch();
  const { globalLanguage } = useSelector((state: RootState) => state.setup);

  useLayoutEffect(() => {
    const language = getSystemLanguage();
    setupUserLanguage(language);
  }, []);
  
  const getSystemOriginLanguage = () => {
    let systemLanguage = '';
    if (Platform.OS === 'ios') {
      systemLanguage = NativeModules.SettingsManager.settings.AppleLocale
      || NativeModules.SettingsManager.settings.AppleLanguages[0];
    } else {
      NativeModules.I18nManager.localeIdentifier;
    }
    
    const language = systemLanguage.toLowerCase().indexOf('ko') >= 0 ? 'KR' : 'EN';
    return language;
  }
  
  const setupUserLanguage = async (systemLanguage: LanguageTypes) => {
    const savedLanguage = await storage.getItem('language');
    await updateUserDefaultLanguage(systemLanguage);
  }
  
  const updateUserDefaultLanguage = async (lang: LanguageTypes) => {
    dispatch(changeGlobalLanguage(lang));
    await storage.setItem('language', lang);
  }
  
  return { language: globalLanguage };
}

 

store는 각자 구성해보도록 하자.

단순히 KR -> EN -> ... 등으로 string 변경만 해주는 수준이니

어렵지 않게 설정 가능할것이다.

반응형
반응형

[solved path issue when app is updated!!]

 

react-native-sound의

issue를 아무리 뒤져봐도 찾을수가 없지만

내게 이슈로 다가왔던

 

업데이트 후 이전 버전에서 다루던

로컬 음원파일이 재생이 안되는 이슈를 해결했다.

 

[이전 방식]

1. 음원 생성

react-native-audio로

로컬에 녹음된 음원을 저장하고

해당 경로를 저장한다.

 

2. 음원 재생

저장된 경로로 부터

음원 파일을 불러와 재생한다.

 

3. 결과

일반적인 경우 문제없으나

앱 업데이트 후 재생이 안되는 이슈 발생

 

 

[문제 해결한 방식]

1. 음원 생성

react-native-audio로

로컬에 녹음된 음원을 저장하고

해당 파일명만 저장한다.

 

2. 음원 재생

저장된 파일명과 AudioUtils.LibraryDirectoryPath를 이용해

음원 파일을 불러와 재생한다.

 

3. 결과

일반적인 경우에도

앱 업데이트 후에도 재생이 잘 된다.

반응형
반응형

background processing 설정 후

빌드를 진행하는 도중

info.plist 설정 관련 에러가 발생했다.

 

 

 위 기능을 사용하기 위해서는

이 설정을 추가적으로 해주어야 한다.

반응형
반응형

(ios에서) 앱을 껐다 다시 켜면

react-native-sound의 실행이

정상적으로 이뤄지지 않는 경우가 발생했다.

 

나의 경우에는

react-native-audio와 sound 두개를 모두 사용하는데

두개를 함께 쓰는 환경에서는

playback 셋팅을 해주지 않으면 안된다고한다.

 

https://github.com/zmxv/react-native-sound/issues/42

 

Sound playing via Receiver instead of phone speaker in IOS · Issue #42 · zmxv/react-native-sound

I have no problems with playing/manipulating the sound file in IOS, but the sound is coming from the receiver so it's not practical. I am using the latest version of this module, testing on Iph...

github.com


정말 어렵게 위의 이슈를 발견했다.

 

Sound.setCategory('Playback')

 

요것 한줄이면 이슈 해결된다.

 

playback 기능을 정상적으로 사용하기 위해서는

이렇게 xcode -> signing & capabilities에서

audio background mode를 활성화 시켜주어야한다.

 

반응형