1. 이전 포스팅 확인하기

 

https://growingsaja.tistory.com/936

 

 

 

 

 

2. 목표

 

    a. 시간에 맞게 환율 정보를 가져와 저장하는 기능 구현

    b. 환율 업데이트 시간을 "x분 y초 전" 형태로 live 노출

    c. 한국 화폐 KRW 기준의 환율 정보도 함께 노출

 

 

 

 

 

3. OpenExchangeRates 소스코드 수정

 

// vim OpenExchangeRates.h

@interface OpenExchangeRates : NSObject

// ****** 활용하게 될 krw 관련 주요 데이터 ****** //
// 최신 정보가 업데이트된 시간
@property (readwrite, strong, nonatomic) NSString *recentUpdateDatetime;
// 최신 usdkrw 환율
@property (readwrite, strong, nonatomic) NSString *usdkrw;
// 최신 usdkrw 환율 기준 단위 - ex. USD
@property (readwrite, strong, nonatomic) NSString *paymentCurrency;
// 출처 = OpenExchangeRates
@property (readwrite, strong, nonatomic) NSString *provider;

// ****** 활용하게 될 글로벌 관련 주요 데이터 ****** //
// 최신 여러 currency 환율
@property (readwrite, strong, nonatomic) NSMutableDictionary *usdRates;

+(instancetype)sharedInstance;
-(void)fetchDataWithCompletion;

@end

 

// vim OpenExchangeRates.m

#import <Foundation/Foundation.h>
#import "OpenExchangeRates.h"
// api 통신
#import "APIManager.h"
// api 정보 가져오기 위해
#import "DefaultLoader.h"
// unix를 string으로 timestamp 데이터 변경
#import "CRTunixToString.h"

@implementation OpenExchangeRates

+(instancetype)sharedInstance {
    
    static OpenExchangeRates *sharedInstance = nil;
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        sharedInstance = [[self alloc] init];
        sharedInstance.usdRates = [@{} mutableCopy];
    });
    return sharedInstance;
}

// 매 1시간마다 업데이트되며, 00분 00초부터 00분 30초 사이에 업데이트된 수치가 노출됨
-(void)fetchDataWithCompletion {
    // **************************************** [Start] api 콜 준비 **************************************** //
    NSDictionary *openExchangeRates = [DefaultLoader sharedInstance].ratesApi[@"OpenExchangeRates"];
    NSArray *paymentCurrencyList = [DefaultLoader sharedInstance].serviceRecentRatesSetting[@"paymentCurrencyList"];
    APIManager* tryApiCall = [APIManager sharedInstance];
    // **************************************** [Start] Binance 데이터 가져오기 **************************************** //
    NSString *apiURL = [openExchangeRates[@"url"] stringByAppendingString:@"?app_id="];
    apiURL = [apiURL stringByAppendingString:openExchangeRates[@"key"]];
    [tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error.localizedDescription);
        } else {
            // 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
            if ([[jsonResponse allKeys] containsObject:@"error"]) {
                // error 발생한 경우 error, status, message라는 키값을 포함합니다.
                NSLog(@"[ERROR] status code 4** error");
            } else {
                // 에러가 발생하지 않은 경우 error, status, message라는 키값을 가지지 않으며 timestamp, base, rates라는 키값을 포함합니다.
                // ****** api return된 timestamp 가져와서 string으로 변경 = 환율 최신 수치 ****** //
                NSNumber *unixTimestampRecent = jsonResponse[@"timestamp"];
                // unix를 string으로 바꾸기
                CRTunixToString *updateDatetime = [CRTunixToString sharedInstance];
                [updateDatetime stringFromUnix:unixTimestampRecent andFormat:@"yyyy-MM-dd HH:mm:ss"];
                if ([self.recentUpdateDatetime isEqual:updateDatetime.stringDateTime]) {
                    // " 내 최근 update 시간 == api 최신 update 시간 " 인 경우
                    // 최근 업데이트 시간과 같으면 어차피 같은 데이터이므로 업데이트 미진행
                    // 업데이트할게 없음. api call을 더 적게 해야한다고 알리는 NSLog입니다.
                    // 1달에 1,000건만 가능하니까 어차피 업데이트할 게 없는 상황인데도 api call을 했기 때문에 여기 WARN 문구를 출력합니다.
                    NSLog(@"[WARN] OpenExchangeRates API Call Is Wasted! recentUpdateTime: %@, now: %@", self.recentUpdateDatetime, updateDatetime.stringDateTime);
                } else {
                    NSLog(@"[INFO] OpenExchangeRates update confirmed! recentUpdateTime : %@, now: %@", self.recentUpdateDatetime, updateDatetime.stringDateTime);
                    // 최근 환율 업데이트 시간 변경됨 감지
                    // 최근 업데이트 시간 최신화 from api
                    self.recentUpdateDatetime = updateDatetime.stringDateTime;
                    // 최근 업데이트 환율값 최신화 (소수점 2자리까지 반올림 진행)
                    NSDictionary *ratesNow = jsonResponse[@"rates"];
                    
                    for (NSString *paymentCurrency in paymentCurrencyList) {
                        // 글로벌 환율 size에 따라 반올림해서 저장 진행
                        float usdRateFloat = [ratesNow[paymentCurrency] floatValue];
                        if (usdRateFloat >= 10000) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.1f", usdRateFloat];
                        } else if (usdRateFloat >= 1000) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdRateFloat];
                        } else if (usdRateFloat >= 100) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.3f", usdRateFloat];
                        } else if (usdRateFloat >= 10) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.4f", usdRateFloat];
                        } else if (usdRateFloat >= 1) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.5f", usdRateFloat];
                        } else if (usdRateFloat >= 0.1) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.6f", usdRateFloat];
                        } else if (usdRateFloat >= 0.01) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.7f", usdRateFloat];
                        } else if (usdRateFloat >= 0.001) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.8f", usdRateFloat];
                        } else if (usdRateFloat >= 0.0001) {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%.9f", usdRateFloat];
                        } else {
                            self.usdRates[paymentCurrency] = [NSString stringWithFormat:@"%f", usdRateFloat];
                        }
                    }
                    // 한국 환율
                    float usdkrwFloat = [ratesNow[@"KRW"] floatValue];
                    if (usdkrwFloat >= 10000) {
                        self.usdkrw = [NSString stringWithFormat:@"%.1f", usdkrwFloat];
                    } else if (usdkrwFloat >= 1000) {
                        self.usdkrw = [NSString stringWithFormat:@"%.2f", usdkrwFloat];
                    } else if (usdkrwFloat >= 100) {
                        self.usdkrw = [NSString stringWithFormat:@"%.3f", usdkrwFloat];
                    } else if (usdkrwFloat >= 10) {
                        self.usdkrw = [NSString stringWithFormat:@"%.4f", usdkrwFloat];
                    } else if (usdkrwFloat >= 1) {
                        self.usdkrw = [NSString stringWithFormat:@"%.5f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.1) {
                        self.usdkrw = [NSString stringWithFormat:@"%.6f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.01) {
                        self.usdkrw = [NSString stringWithFormat:@"%.7f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.001) {
                        self.usdkrw = [NSString stringWithFormat:@"%.8f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.0001) {
                        self.usdkrw = [NSString stringWithFormat:@"%.9f", usdkrwFloat];
                    } else {
                        self.usdkrw = [NSString stringWithFormat:@"%f", usdkrwFloat];
                    }
                    // payment currency 최신화 (ex.KRW)
                    self.paymentCurrency = @"KRW";
                    // 출처
                    self.provider = @"OpenExchangeRates";
                    NSLog(@"[INFO] OpenExchangeRates updated! recentUpdateTime : %@, usdkrw : %@", self.recentUpdateDatetime, self.usdkrw);
                }
            }
        }
    }];
}
@end

 

 

 

 

 

4. DunamuQuotation 소스 코드 수정

 

// vim DunamuQuotation.h

@interface DunamuQuotation : NSObject

// ****** 활용하게 될 krw 관련 주요 데이터 ****** //
// 최근 데이터 일시
@property (readwrite, strong, nonatomic) NSString *recentDateTime;
// 최근 데이터 환율
@property (readwrite, strong, nonatomic) NSString *usdkrw;
// 최근 데이터 거래 기준 화폐
@property (readwrite, strong, nonatomic) NSString *paymentCurrency;
// 시가
@property (readwrite, strong, nonatomic) NSString *openUsdkrw;
// 한국 기준 오늘의 변동가
@property (readwrite, strong, nonatomic) NSString *changeUsdkrw;
// 한국 기준 오늘의 변동률
@property (readwrite, strong, nonatomic) NSString *changePercentUsdkrw;
// 출처
@property (readwrite, strong, nonatomic) NSString *provider;

// ****** 활용하게 될 글로벌 관련 주요 데이터 ****** //
// 최신 여러 currency 환율
@property (readwrite, strong, nonatomic) NSMutableDictionary *ratesData;


+(instancetype)sharedInstance;
-(void)fetchDataWithCompletion;

@end

 

// vim DunamuQuotation.m

#import <Foundation/Foundation.h>
#import "DunamuQuotation.h"
// api 통신
#import "APIManager.h"
// 정보 가져오기
#import "DefaultLoader.h"

@implementation DunamuQuotation

+(instancetype)sharedInstance {
    
    static DunamuQuotation *sharedInstance = nil;
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        sharedInstance = [[self alloc] init];
        sharedInstance.ratesData = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentUsdRates"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentOpenUsdRates"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentUsdChange"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentUsdChangePercent"] = [@{} mutableCopy];
        // krw 기본 정보
        sharedInstance.ratesData[@"country"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"provider"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"currencyName"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"currencyUnit"] = [@{} mutableCopy];
        // krw 기준
        sharedInstance.ratesData[@"recentKrwRates"] = [@{} mutableCopy];
        // KRW api에서의 정보
        sharedInstance.ratesData[@"recentOpenKrwRates"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentKrwChange"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentKrwChangePercent"] = [@{} mutableCopy];
    });
    return sharedInstance;
}

// 매 1시간마다 업데이트되며, 00분 00초부터 00분 30초 사이에 업데이트된 수치가 노출됨
-(void)fetchDataWithCompletion {
    // **************************************** [Start] api 콜 준비 **************************************** //
    NSDictionary *dunamuQuotation = [DefaultLoader sharedInstance].ratesApi[@"DunamuQuotation"];
    NSArray *paymentCurrencyList = [DefaultLoader sharedInstance].serviceRecentRatesSetting[@"paymentCurrencyList"];
    APIManager* tryApiCall = [APIManager sharedInstance];
    /* #################### [Start] open exchange rates KRW 환율 데이터 가져오기 #################### */
    NSString *apiURL = dunamuQuotation[@"url"];
    // 1USD 당 몇KRW인지 알기 위한 url 조합
    apiURL = [apiURL stringByAppendingString:@"KRW"];
    apiURL = [apiURL stringByAppendingString:@"USD"];
    [tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error.localizedDescription);
        } else {
            // 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
            if ([jsonResponse isKindOfClass:[NSDictionary class]]) {
                // error 발생한 경우 error, status, message라는 키값을 포함합니다.
                NSLog(@"[ERROR] DunamuQuotation status: %@, message: %@", jsonResponse[@"status"], jsonResponse[@"message"]);
            } else if ([jsonResponse isKindOfClass:[NSArray class]]) {
                // 정상 처리시, dictionary가 아니라 array입니다.
                if ([jsonResponse isEqual:@[]]) {
                    // 빈 array가 나오면 해당 api 비활성화 또는 잘못된 요청을 한 것입니다.
                    NSLog(@"[ERROR] DunamuQuotation empty array return.");
                } else {
                    // 정상 return으로 환율 정보 업데이트, 사용하기 편한 형태로 변경
                    NSDictionary *result = ((NSArray *)jsonResponse)[0];
                    // 최근 환율 업데이트 시간 정보
                    NSString *recentDateTime = [result[@"date"] stringByAppendingString:@" "];
                    recentDateTime = [recentDateTime stringByAppendingString:result[@"time"]];
                    self.recentDateTime = recentDateTime;
                    // 환율
                    float usdkrwFloat = [result[@"basePrice"] floatValue];
                    if (usdkrwFloat >= 10000) {
                        self.usdkrw = [NSString stringWithFormat:@"%.1f", usdkrwFloat];
                    } else if (usdkrwFloat >= 1000) {
                        self.usdkrw = [NSString stringWithFormat:@"%.2f", usdkrwFloat];
                    } else if (usdkrwFloat >= 100) {
                        self.usdkrw = [NSString stringWithFormat:@"%.3f", usdkrwFloat];
                    } else if (usdkrwFloat >= 10) {
                        self.usdkrw = [NSString stringWithFormat:@"%.4f", usdkrwFloat];
                    } else if (usdkrwFloat >= 1) {
                        self.usdkrw = [NSString stringWithFormat:@"%.5f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.1) {
                        self.usdkrw = [NSString stringWithFormat:@"%.6f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.01) {
                        self.usdkrw = [NSString stringWithFormat:@"%.7f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.001) {
                        self.usdkrw = [NSString stringWithFormat:@"%.8f", usdkrwFloat];
                    } else if (usdkrwFloat >= 0.0001) {
                        self.usdkrw = [NSString stringWithFormat:@"%.9f", usdkrwFloat];
                    } else {
                        self.usdkrw = [NSString stringWithFormat:@"%f", usdkrwFloat];
                    }
                    // 환율 변동가
                    self.changeUsdkrw = [NSString stringWithFormat:@"%@", result[@"signedChangePrice"]];
                    // 한국 환율 시가 = api에서 주는 openingPrice 정보를 사용하는게 아니라 "현재가-변동가"로 계산해서 사용합니다.
                    self.openUsdkrw = [NSString stringWithFormat:@"%.2f", [self.usdkrw floatValue] - [self.changeUsdkrw floatValue]];
                    // 환율 변동률 소수 2째자리수까지 반올림
                    self.changePercentUsdkrw = [NSString stringWithFormat:@"%.2f", [result[@"signedChangeRate"] floatValue] * 100];
                    // payment currency 추출해서 저장 : KRW, EUR 등
                    // ex. "name": "미국 (USD/KRW)" => 제일 뒤에 있는 문자 )를 삭제하고, / 기준으로 뒤쪽 string 가져오기
                    self.paymentCurrency = result[@"currencyUnit"];
                    // 데이터 출처
                    self.provider = result[@"provider"];
                    NSLog(@"[INFO] DunamuQuotation updated : %@ - %@", self.recentDateTime, self.usdkrw);
                    // api로 받은 데이터 깔끔한 dictionary로 가공하기
                }
            }
        }
    }];
    
    /* #################### [Start] open exchange rates global 환율 데이터 가져오기 #################### */
    for (NSString *paymentCurrency in paymentCurrencyList) {
        apiURL = dunamuQuotation[@"url"];
        // 1USD 당 몇{paymentCurrency}}인지 알기 위한 url 조합
        apiURL = [apiURL stringByAppendingString:paymentCurrency];
        apiURL = [apiURL stringByAppendingString:@"USD"];
        [tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
            if (error) {
                NSLog(@"Error: %@", error.localizedDescription);
            } else {
                // 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
                if ([jsonResponse isKindOfClass:[NSDictionary class]]) {
                    // error 발생한 경우 error, status, message라는 키값을 포함합니다.
                    NSLog(@"[ERROR] DunamuQuotation USD status: %@, message: %@", jsonResponse[@"status"], jsonResponse[@"message"]);
                } else if ([jsonResponse isKindOfClass:[NSArray class]]) {
                    // 정상 처리시, dictionary가 아니라 array입니다.
                    if ([jsonResponse isEqual:@[]]) {
                        // 빈 array가 나오면 해당 화폐에 대한 정보를 api에서 제공하지 않는 것입니다. 해당 화폐 정보는 skip해줍니다.
                        NSLog(@"[ERROR] DunamuQuotation USD empty array return. %@", paymentCurrency);
                        self.ratesData[@"recentUsdRates"][paymentCurrency] = @"-";
                        self.ratesData[@"recentOpenUsdRates"][paymentCurrency] = @"-";
                        self.ratesData[@"recentUsdChange"][paymentCurrency] = @"-";
                        self.ratesData[@"recentUsdChangePercent"][paymentCurrency] = @"-";
                        self.ratesData[@"country"][paymentCurrency] = @"-";
                        self.ratesData[@"provider"][paymentCurrency] = @"-";
                        self.ratesData[@"currencyName"][paymentCurrency] = @"-";
                        self.ratesData[@"currencyUnit"][paymentCurrency] = @"-";
                        self.ratesData[@"recentKrwRates"][paymentCurrency] = @"-";
                    } else {
                        // 정상 return으로 환율 정보 업데이트, 사용하기 편한 형태로 변경
                        NSDictionary *result = ((NSArray *)jsonResponse)[0];
                        // 최근 환율 업데이트 시간 정보
                        NSString *recentDateTime = [result[@"date"] stringByAppendingString:@" "];
                        recentDateTime = [recentDateTime stringByAppendingString:result[@"time"]];
                        self.recentDateTime = recentDateTime;
                        // 환율
                        float usdRateFloat = [result[@"basePrice"] floatValue];
                        if (usdRateFloat >= 10000) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.1f", usdRateFloat];
                        } else if (usdRateFloat >= 1000) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdRateFloat];
                        } else if (usdRateFloat >= 100) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.3f", usdRateFloat];
                        } else if (usdRateFloat >= 10) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.4f", usdRateFloat];
                        } else if (usdRateFloat >= 1) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.5f", usdRateFloat];
                        } else if (usdRateFloat >= 0.1) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.6f", usdRateFloat];
                        } else if (usdRateFloat >= 0.01) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.7f", usdRateFloat];
                        } else if (usdRateFloat >= 0.001) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.8f", usdRateFloat];
                        } else if (usdRateFloat >= 0.0001) {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.9f", usdRateFloat];
                        } else {
                            self.ratesData[@"recentUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%f", usdRateFloat];
                        }
                        // ****** 글로벌 각 화폐 환율 변동가 ****** //
                        self.ratesData[@"recentUsdChange"][paymentCurrency] = [NSString stringWithFormat:@"%@", result[@"signedChangePrice"]];
                        // 글로벌 각 화폐 환율 시가 = api에서 주는 openingPrice 정보를 사용하는게 아니라 "현재가-변동가"로 계산해서 사용합니다.
                        self.ratesData[@"recentOpenUsdRates"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdRateFloat - [result[@"signedChangePrice"] floatValue]];
                        // ****** 글로벌 각 화폐 환율 변동률 소수 2째자리수까지 반올림 ****** //
                        self.ratesData[@"recentUsdChangePercent"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", [result[@"signedChangeRate"] floatValue] * 100];
                        // ****** 글로벌 각 화폐의 국가 ****** //
                        if (result[@"country"] == [NSNull null]) {
                            NSString *currencyCode = result[@"currencyCode"];
                            if ([currencyCode isEqual:@"CNH"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"홍콩";
                            } else if ([currencyCode isEqual: @"CLP"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"칠레";
                            } else if ([currencyCode isEqual: @"COP"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"콜롬비아";
                            } else if ([currencyCode isEqual: @"OMR"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"오만";
                            } else if ([currencyCode isEqual: @"RON"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"루마니아";
                            } else if ([currencyCode isEqual: @"DZD"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"알제리";
                            } else if ([currencyCode isEqual: @"ETB"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"에티오피아";
                            } else if ([currencyCode isEqual: @"FJD"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"피지";
                            } else if ([currencyCode isEqual: @"KES"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"케냐";
                            } else if ([currencyCode isEqual: @"KHR"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"캄보디아";
                            } else if ([currencyCode isEqual: @"LKR"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"스리랑카";
                            } else if ([currencyCode isEqual: @"LYD"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"리비아";
                            } else if ([currencyCode isEqual: @"MMK"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"미얀마";
                            } else if ([currencyCode isEqual: @"MOP"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"마카오";
                            } else if ([currencyCode isEqual: @"NPR"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"네팔";
                            } else if ([currencyCode isEqual: @"TZS"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"탄자니아";
                            } else if ([currencyCode isEqual: @"UZS"]) {
                                self.ratesData[@"country"][paymentCurrency] = @"우즈베키스탄";
                            } else {
                                // 예외처리 목록에 없으면 currencyCode로 대체
                                self.ratesData[@"country"][paymentCurrency] = result[@"currencyCode"];
                            }
                        } else {
                            // data 있으면 그대로
                            if ([result[@"country"] isEqual:@"미국"]) {
                                // 한국 api라서 그런지, 한국KRW정보인데 미국이라고 나와서 수정 적용
                                self.ratesData[@"country"][paymentCurrency] = @"대한민국";
                            } else {
                                // 이외 api는 데이터 그대로 사용
                                self.ratesData[@"country"][paymentCurrency] = result[@"country"];
                            }
                        }
                        // ****** 데이터 출처 ****** //
                        if (result[@"provider"] == [NSNull null]) {
                            // 없으면 "-"
                            self.ratesData[@"provider"][paymentCurrency] = @"-";
                        } else {
                            // 있으면 있는거 가져오기
                            self.ratesData[@"provider"][paymentCurrency] = result[@"provider"];
                        }
                        // ****** 화폐 단위 한국어로 ****** //
                        if (result[@"currencyName"] == [NSNull null]) {
                            NSString *currencyCode = result[@"currencyCode"];
                            if ([currencyCode isEqual:@"CNH"]) {
                                self.ratesData[@"currencyName"][paymentCurrency] = @"위안";
                            } else if ([currencyCode isEqual: @"CLP"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"페소";
                           } else if ([currencyCode isEqual: @"COP"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"페소";
                           } else if ([currencyCode isEqual: @"OMR"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"리알";
                           } else if ([currencyCode isEqual: @"RON"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"레위";
                           } else if ([currencyCode isEqual: @"DZD"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"디나르";
                           } else if ([currencyCode isEqual: @"ETB"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"비르";
                           } else if ([currencyCode isEqual: @"FJD"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"달러";
                           } else if ([currencyCode isEqual: @"KES"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"실링";
                           } else if ([currencyCode isEqual: @"KHR"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"리얄";
                           } else if ([currencyCode isEqual: @"LKR"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"루피";
                           } else if ([currencyCode isEqual: @"LYD"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"디나르";
                           } else if ([currencyCode isEqual: @"MMK"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"키얏";
                           } else if ([currencyCode isEqual: @"MOP"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"파타카";
                           } else if ([currencyCode isEqual: @"NPR"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"루피";
                           } else if ([currencyCode isEqual: @"TZS"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"실링";
                           } else if ([currencyCode isEqual: @"UZS"]) {
                               self.ratesData[@"currencyName"][paymentCurrency] = @"솜";
                           } else {
                               // 예외처리 목록에 없으면 영어로 = CurrencyCode
                               self.ratesData[@"currencyName"][paymentCurrency] = result[@"currencyCode"];
                           }
                        } else {
                            // 있으면 그대로
                            self.ratesData[@"currencyName"][paymentCurrency] = result[@"currencyName"];
                        }
                    }
                }
            }
        }];
    }
    
    /* #################### [Start] open exchange rates KRW global 환율 데이터 가져오기 #################### */
    
    for (NSString *paymentCurrency in paymentCurrencyList) {
        if ([paymentCurrency isEqual:@"KRW"]) {
            // KRW는 api 미제공일뿐더러 이미 정보가 다 있어서 미진행
            self.ratesData[@"recentKrwRates"][@"KRW"] = @"1.00";
            self.ratesData[@"recentKrwChange"][@"KRW"] = @"0.00";
            self.ratesData[@"recentKrwChangePercent"][@"KRW"] = @"0.00";
            self.ratesData[@"recentOpenKrwRates"][@"KRW"] = @"1.00";
            self.ratesData[@"currencyUnit"][@"KRW"] = @1;
        } else {
            apiURL = dunamuQuotation[@"url"];
            // 1USD 당 몇{paymentCurrency}}인지 알기 위한 url 조합
            apiURL = [apiURL stringByAppendingString:@"KRW"];
            apiURL = [apiURL stringByAppendingString:paymentCurrency];
            [tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
                if (error) {
                    NSLog(@"Error: %@", error.localizedDescription);
                } else {
                    // 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
                    if ([jsonResponse isKindOfClass:[NSDictionary class]]) {
                        // error 발생한 경우 error, status, message라는 키값을 포함합니다.
                        NSLog(@"[ERROR] DunamuQuotation KRW status: %@, message: %@", jsonResponse[@"status"], jsonResponse[@"message"]);
                    } else if ([jsonResponse isKindOfClass:[NSArray class]]) {
                        // 정상 처리시, dictionary가 아니라 array입니다.
                        if ([jsonResponse isEqual:@[]]) {
                            // 빈 array가 나오면 해당 화폐에 대한 정보를 api에서 제공하지 않는 것입니다. 해당 화폐 정보는 skip해줍니다.
                            NSLog(@"[ERROR] DunamuQuotation KRW empty array return. %@", paymentCurrency);
                            self.ratesData[@"recentKrwChange"][paymentCurrency] = @"-";
                            self.ratesData[@"recentKrwChangePercent"][paymentCurrency] = @"-";
                        } else {
                            // 정상 return으로 환율 정보 업데이트, 사용하기 편한 형태로 변경
                            NSDictionary *result = ((NSArray *)jsonResponse)[0];
                            // ****** 한국돈 환전시 최소 화폐 단위 ex. 100엔당 얼마원 ****** //
                            self.ratesData[@"currencyUnit"][paymentCurrency] = result[@"currencyUnit"];
                            // ****** 한국 화폐 원 기준의 환율 호가 정보 recentKrwRates ****** //
                            float krwRateFloat;
                            if ([result[@"basePrice"]  isEqual: @0] || result[@"basePrice"] == [NSNull null]) {
                                // 0이거나 존재하지 않으면 계산해서 구해보기
                                if ([result[@"cashBuyingPrice"] floatValue] == 0 && [result[@"cashSellingPrice"] floatValue] == 0) {
                                    // cash 관련 정보로 평균치 내서 도출
                                    krwRateFloat = ([result[@"cashBuyingPrice"] floatValue] + [result[@"cashSellingPrice"] floatValue]) / 2;
                                } else {
                                    // cash 정보 없으면 그냥 0 넣어주기
                                    krwRateFloat = [@0 floatValue];
                                }
                            } else {
                                // 정상인 경우
                                if ([paymentCurrency isEqual:@"KRW"]) {
                                    // 한국은 항상 1이기 때문에 예외처리
                                    self.ratesData[@"recentKrwRates"][paymentCurrency] = @"1.00";
                                } else {
                                    // 한국 이외 국가는 계속 변동되기 때문에 계산해서 저장
                                    krwRateFloat = [result[@"basePrice"] floatValue];
                                    // 해당 값을 size에 맞게 반올림해서 저장
                                    NSString *krwRateString;
                                    if (krwRateFloat >= 10000) {
                                        krwRateString = [NSString stringWithFormat:@"%.1f", krwRateFloat];
                                    } else if (krwRateFloat >= 1000) {
                                        krwRateString = [NSString stringWithFormat:@"%.2f", krwRateFloat];
                                    } else if (krwRateFloat >= 100) {
                                        krwRateString = [NSString stringWithFormat:@"%.3f", krwRateFloat];
                                    } else if (krwRateFloat >= 10) {
                                        krwRateString = [NSString stringWithFormat:@"%.4f", krwRateFloat];
                                    } else if (krwRateFloat >= 1) {
                                        krwRateString = [NSString stringWithFormat:@"%.5f", krwRateFloat];
                                    } else if (krwRateFloat >= 0.1) {
                                        krwRateString = [NSString stringWithFormat:@"%.6f", krwRateFloat];
                                    } else if (krwRateFloat >= 0.01) {
                                        krwRateString = [NSString stringWithFormat:@"%.7f", krwRateFloat];
                                    } else if (krwRateFloat >= 0.001) {
                                        krwRateString = [NSString stringWithFormat:@"%.8f", krwRateFloat];
                                    } else if (krwRateFloat >= 0.0001) {
                                        krwRateString = [NSString stringWithFormat:@"%.9f", krwRateFloat];
                                    } else {
                                        krwRateString = [NSString stringWithFormat:@"%f", krwRateFloat];
                                    }
                                    // String 값 저장
                                    self.ratesData[@"recentKrwRates"][paymentCurrency] = krwRateString;
                                }
                                
                            }
                            // ****** 글로벌 각 화폐 환율 시작가, 변동가, 변동률 ****** //
                            if (result[@"provider"] == [NSNull null] && [result[@"signedChangePrice"] isEqual:@0]) {
                                // krw 변동 정보는 추정치임! 왜나하면 과거의 환율 정보를 알 수 없기 때문에, 현재의 환율 정보로 역추산해서 오차가 발생할 수 밖에 없음
                                // ****** krw 기준 현재 환율 가져다쓰기 ****** //
                                float krwRateFloat = [result[@"basePrice"] floatValue];
                                // 해당 값을 size에 맞게 반올림해서 저장
                                NSString *krwRateString;
                                if (krwRateFloat >= 10000) {
                                    krwRateString = [NSString stringWithFormat:@"%.1f", krwRateFloat];
                                } else if (krwRateFloat >= 1000) {
                                    krwRateString = [NSString stringWithFormat:@"%.2f", krwRateFloat];
                                } else if (krwRateFloat >= 100) {
                                    krwRateString = [NSString stringWithFormat:@"%.3f", krwRateFloat];
                                } else if (krwRateFloat >= 10) {
                                    krwRateString = [NSString stringWithFormat:@"%.4f", krwRateFloat];
                                } else if (krwRateFloat >= 1) {
                                    krwRateString = [NSString stringWithFormat:@"%.5f", krwRateFloat];
                                } else if (krwRateFloat >= 0.1) {
                                    krwRateString = [NSString stringWithFormat:@"%.6f", krwRateFloat];
                                } else if (krwRateFloat >= 0.01) {
                                    krwRateString = [NSString stringWithFormat:@"%.7f", krwRateFloat];
                                } else if (krwRateFloat >= 0.001) {
                                    krwRateString = [NSString stringWithFormat:@"%.8f", krwRateFloat];
                                } else if (krwRateFloat >= 0.0001) {
                                    krwRateString = [NSString stringWithFormat:@"%.9f", krwRateFloat];
                                } else {
                                    krwRateString = [NSString stringWithFormat:@"%f", krwRateFloat];
                                }
                                // String 값 저장
                                self.ratesData[@"recentKrwRates"][paymentCurrency] = krwRateString;
                                
                                // ****** krw 기준 과거의 환율 구해서 krw 시작 가격을 usd로부터 역추산하기 ****** //
                                float recentOpenKrwRate = [self.openUsdkrw floatValue] / [self.ratesData[@"recentOpenUsdRates"][paymentCurrency] floatValue] * [result[@"currencyUnit"] floatValue];
                                // 해당 값을 size에 맞게 반올림해서 저장
                                if (krwRateFloat >= 10000) {
                                    krwRateString = [NSString stringWithFormat:@"%.1f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 1000) {
                                    krwRateString = [NSString stringWithFormat:@"%.2f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 100) {
                                    krwRateString = [NSString stringWithFormat:@"%.3f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 10) {
                                    krwRateString = [NSString stringWithFormat:@"%.4f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 1) {
                                    krwRateString = [NSString stringWithFormat:@"%.5f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 0.1) {
                                    krwRateString = [NSString stringWithFormat:@"%.6f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 0.01) {
                                    krwRateString = [NSString stringWithFormat:@"%.7f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 0.001) {
                                    krwRateString = [NSString stringWithFormat:@"%.8f", recentOpenKrwRate];
                                } else if (krwRateFloat >= 0.0001) {
                                    krwRateString = [NSString stringWithFormat:@"%.9f", recentOpenKrwRate];
                                } else {
                                    krwRateString = [NSString stringWithFormat:@"%f", recentOpenKrwRate];
                                }
                                // String 값 저장
                                self.ratesData[@"recentOpenKrwRates"][paymentCurrency] = krwRateString;
                                
                                // ****** krw 기준 변동가 구하기 ****** //
                                float usdkrwChange = [result[@"basePrice"] floatValue] - recentOpenKrwRate;
                                self.ratesData[@"recentKrwChange"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdkrwChange];
                                // ****** krw 기준 변동률 구하기 ****** //
                                float usdkrwChangePercent = usdkrwChange / recentOpenKrwRate * 100;
                                self.ratesData[@"recentKrwChangePercent"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdkrwChangePercent];
                            } else {
                                // 이외의 경우 데이터 정상인 것이므로 그대로 사용
                                self.ratesData[@"recentKrwChange"][paymentCurrency] = [NSString stringWithFormat:@"%@", result[@"signedChangePrice"]];
                                self.ratesData[@"recentKrwChangePercent"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", [result[@"signedChangeRate"] floatValue] * 100];
                                float recentOpenKrwRates = [result[@"basePrice"] floatValue] - [result[@"signedChangePrice"] floatValue];
                                self.ratesData[@"recentOpenKrwRates"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", recentOpenKrwRates];
                            }
                        }
                    }
                }
            }];
        }
    }
}
@end

 

 

 

 

 

5. ServiceRecentRates 소스 코드 수정

 

// vim ServiceRecentRates.h

@interface ServiceRecentRates : NSObject

// ****** 활용하게 될 krw 관련 주요 데이터 ****** //
// 최근 업데이트 시간
@property (readwrite, strong, nonatomic) NSString *recentDateTime;
// 환율 호가
@property (readwrite, strong, nonatomic) NSString *usdkrw;
// 환율 변동가격
@property (readwrite, strong, nonatomic) NSString *changeUsdkrw;
// 환율 변동률
@property (readwrite, strong, nonatomic) NSString *changePercentUsdkrw;
// 환율 변동 기준 시가
@property (readwrite, strong, nonatomic) NSString *openUsdkrw;
// 환율 기준 화폐
@property (readwrite, strong, nonatomic) NSString *paymentCurrency;
// 출처
@property (readwrite, strong, nonatomic) NSString *provider;

// ****** 활용하게 될 글로벌 관련 주요 데이터 ****** //
// 글로벌 환율 정보
@property (readwrite, strong, nonatomic) NSMutableDictionary *ratesData;

// ****** 시작 가격 룰 ****** //
// 오늘 날짜
@property (readwrite, strong, nonatomic) NSString *nowDate;
// 현재 시간
@property (readwrite, strong, nonatomic) NSString *nowTime;



+(instancetype)sharedInstance;

-(void)fetchData;


@end

 

// vim ServiceRecentRates.m

#import <Foundation/Foundation.h>
#import "ServiceRecentRates.h"
// 외부 api call 조건식에 현재 시간이 들어가서 사용
#import "DateTime.h"
// 외부 api 구현 부문 빼둔 클래스 사용
#import "OpenExchangeRates.h"
#import "DunamuQuotation.h"

@implementation ServiceRecentRates

+(instancetype) sharedInstance {
    
    static ServiceRecentRates *sharedInstance = nil;
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        sharedInstance = [[self alloc] init];
        sharedInstance.ratesData = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentUsdRates"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentOpenUsdRates"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentUsdChange"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentUsdChangePercent"] = [@{} mutableCopy];
        // 기본 공통 적용 정보
        sharedInstance.ratesData[@"country"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"provider"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"currencyName"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"currencyUnit"] = [@{} mutableCopy];
        // krw 정보
        sharedInstance.ratesData[@"recentKrwRates"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentOpenKrwRates"] = [@{} mutableCopy];
        // 가공해서 제작
        sharedInstance.ratesData[@"recentKrwChange"] = [@{} mutableCopy];
        sharedInstance.ratesData[@"recentKrwChangePercent"] = [@{} mutableCopy];
    });
    return sharedInstance;
}
 
-(void)fetchData {
    // **************************************** 현재 시간을 참조하여 기준이 되는 정보 선별 **************************************** //
    // ****** 현재 가격 룰 ****** //
    // 00:00:00 ~ 08:24:59 -> Open Exchange Rates
    // 08:25:00 ~ 23:59:59 -> 두나무의 하나은행
    // ****** 시작 가격 룰 ****** //
    // 00:00:00 ~ 08:24:59 -> 전 영업일 종가 = 23:59:59 두나무의 하나은행의 최종 usdkrw
    // 08:25:00 ~ 23:59:59 -> 두나무의 하나은행 환율 정보 토대로 역계산한 시가 = usdkrw-changePriceUsdkrw
    /* #################### 로직 시작 #################### */
    DateTime *now = [DateTime sharedInstance];
    [now NowKST:@"yyyy-MM-dd (E) HH:mm:ss"];
    _nowDate = [[now.dateTime componentsSeparatedByString:@" "] objectAtIndex:0]; // 2023-07-16 yyyy-MM-dd
    _nowTime = [[now.dateTime componentsSeparatedByString:@" "] objectAtIndex:2]; // 09:05:16 HH:mm:ss
    NSString *nowHour = [[_nowTime componentsSeparatedByString:@":"] objectAtIndex:0]; // 08 HH
    NSString *nowMin = [[_nowTime componentsSeparatedByString:@":"] objectAtIndex:1]; // 25 HH
    if ([nowHour floatValue] < 8) {
        // 00시에서 08시 사이인 경우
        // 과거 데이터 업데이트
        [self DunamuQuotationForPastInfo];
        // 현재 데이터 업데이트
        [self OpenExchangeRatesForRecentInfo];
    } else if ([nowHour floatValue] == 8) {
        // 08시~09시인 경우
        if ([nowMin floatValue] < 25) {
            // 00분 ~ 24분인 경우
            // 과거 데이터 업데이트
            [self DunamuQuotationForPastInfo];
            // 현재 데이터 업데이트
            [self OpenExchangeRatesForRecentInfo];
        } else {
            // 8시 25분 ~ 8시 59분인 경우
            // 과거 & 현재 데이터 한번에 업데이트
            [self DunamuQuotationForAllInfo];
        }
    } else {
        // 09시에서 24시 사이인 경우
        // 과거 & 현재 데이터 한번에 업데이트
        [self DunamuQuotationForAllInfo];
    }
}

/* #################### Open Exchange Rates 데이터를 현재 환율로 사용 #################### */
-(void) OpenExchangeRatesForRecentInfo {
    OpenExchangeRates *usdkrwApi = [OpenExchangeRates sharedInstance];
    // ****** Open Exchange Rates api 콜 조건 구현 ****** //
    // -- 계속 콜하면 api 콜 limit 걸립니다. 현재 1개월에 1,000회가 무료이니 거기까지만 쓰는 것을 추천하며 00분 00초 ~ 30초 사이마다 1시간 단위로 데이터가 최신화되는 점을 감안했습니다.
    NSString *nowMin = [[_nowTime componentsSeparatedByString:@":"] objectAtIndex:1];
    NSString *nowSec = [[_nowTime componentsSeparatedByString:@":"] objectAtIndex:2];
    // ****** 데이터 최신화 시도 ****** //
    if (!usdkrwApi.usdkrw || [[nowMin stringByAppendingString:nowSec] isEqual:@"0030"]) {
        // _usdkrw 데이터가 없거나
        // 매 시의 00분 30초에만 api 콜 진행합니다. 본 코드는 매 특정시간마다 데이터를 업데이트한다는 전제하에서 작성되었으므로, PopluarAssetListVC.m 파일에 "updateCardData"를 실행하는 주기에 따라 수정이 필요할 수 있습니다.
        [usdkrwApi fetchDataWithCompletion];
    } else {
        /* #################### 변경된 데이터 적용 - 한국 관련 정보 #################### */
        // ****** 환율 호가 ****** //
        if (usdkrwApi.usdkrw) {
            _usdkrw = usdkrwApi.usdkrw;
        } else {
            // 데이터 가져오는것 자체에 실패했을 경우 발생하는 예외 처리입니다.
            _usdkrw = @"Load Fail";
        }
        // ****** 업데이트 시간 ****** //
        if (usdkrwApi.recentUpdateDatetime) {
            _recentDateTime = usdkrwApi.recentUpdateDatetime;
        } else {
            // 데이터 가져오는것 자체에 실패했을 경우 발생하는 예외 처리입니다.
            _recentDateTime = [DateTime sharedInstance].dateTime;
        }
        // ****** 기준 화폐 ****** //
        if (usdkrwApi.paymentCurrency) {
            _paymentCurrency = usdkrwApi.paymentCurrency;
        } else {
            // 데이터 가져오는것 자체에 실패했을 경우 발생하는 예외 처리입니다.
            _paymentCurrency = @"💱";
        }
        // ****** 환율 변동가 & 변동률 ****** //
        if (usdkrwApi.usdkrw && _openUsdkrw) {
            // 시가가 있는 경우, 해당 데이터를 활용해 계산
            float changeUsdkrw = [usdkrwApi.usdkrw floatValue]-[_openUsdkrw floatValue];
            float changePercentUsdkrw = changeUsdkrw/[_openUsdkrw floatValue] * 100;
            _changeUsdkrw = [NSString stringWithFormat:@"%.2f", changeUsdkrw];
            _changePercentUsdkrw = [NSString stringWithFormat:@"%.2f", changePercentUsdkrw];
        } else {
            // 데이터 가져오는것 자체에 실패했을 경우 발생하는 예외 처리입니다.
            _changePercentUsdkrw = @"0.00";
        }
        // ****** 출처 ****** //
        _provider = usdkrwApi.provider;
        
        /* #################### 변경된 데이터 적용 - 글로벌 관련 정보 #################### */
        // ****** 환율 호가 ****** //
        if (usdkrwApi.usdRates) {
            _ratesData[@"recentUsdRates"] = usdkrwApi.usdRates;
        } else {
            // 데이터 가져오는것 자체에 실패했을 경우 발생하는 예외 처리입니다.
            _ratesData[@"recentUsdRates"] = [@{} mutableCopy];
        }
        // ****** usd 정보 ****** //
        for (NSString *paymentCurrency in usdkrwApi.usdRates.allKeys) {
            // 각 payment currency에 대해 계산
            float usdRate = [_ratesData[@"recentUsdRates"][paymentCurrency] floatValue];
            float openUsdRate = [_ratesData[@"recentOpenUsdRates"][paymentCurrency] floatValue];
            float openKrwRate = [_ratesData[@"recentOpenKrwRates"][paymentCurrency] floatValue];
            // usd 변동가 계산 및 저장
            float usdRateChange = usdRate - openUsdRate;
            _ratesData[@"recentUsdChange"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdRateChange];
            // usd 변동률 계산 및 저장
            float usdRateChangePercent = usdRateChange / openUsdRate * 100;
            _ratesData[@"recentUsdChangePercent"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", usdRateChangePercent];
            // 기본 공통 적용 정보
            _ratesData[@"provider"][paymentCurrency] = @"OpenExchangeRates";
            
            // krw 기준 환율 정보
            float recentKrwRate = [_usdkrw floatValue] / usdRate * [_ratesData[@"currencyUnit"][paymentCurrency] floatValue];
            _ratesData[@"recentKrwRates"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", recentKrwRate];
            // krw 기준 변동가 계산 및 저장
            float krwRateChange = recentKrwRate - openKrwRate;
            _ratesData[@"recentKrwChange"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", krwRateChange];
            // krw 기준 변동률 계산 및 저장
            float krwRateChangePercent = krwRateChange / openKrwRate * 100;
            _ratesData[@"recentKrwChangePercent"][paymentCurrency] = [NSString stringWithFormat:@"%.2f", krwRateChangePercent];
        }
    }
}

/* #################### Dunamu Quotation 데이터를 과거 데이터로만 사용 #################### */
-(void) DunamuQuotationForPastInfo {
    DunamuQuotation *usdkrwApi = [DunamuQuotation sharedInstance];
    NSString *nowSec = [[_nowTime componentsSeparatedByString:@":"] objectAtIndex:2];
    // ****** 데이터 최신화 시도 ****** //
    if (!_usdkrw || [nowSec isEqual:@"00"]) {
        // 매 분의 00초에만 api 콜 진행합니다. 본 코드는 매 특정시간마다 데이터를 업데이트한다는 전제하에서 작성되었으므로, PopluarAssetListVC.m 파일에 "updateCardData"를 실행하는 주기에 따라 수정이 필요할 수 있습니다.
        [usdkrwApi fetchDataWithCompletion];
    } else {
        // ****** 변경된 데이터 적용 ****** //
        // 환율 과거 데이터에 넣기
        _openUsdkrw = usdkrwApi.usdkrw;
        _ratesData[@"recentOpenUsdRates"] = usdkrwApi.ratesData[@"recentUsdRates"];
        _ratesData[@"recentOpenKrwRates"] = usdkrwApi.ratesData[@"recentKrwRates"];
        // 기본 정보 넣기
        _ratesData[@"country"] = usdkrwApi.ratesData[@"country"];
        // provider는 OpenExchangeRates로 넣어줘야해서 미진행
        _ratesData[@"currencyName"] = usdkrwApi.ratesData[@"currencyName"];
        _ratesData[@"currencyUnit"] = usdkrwApi.ratesData[@"currencyUnit"];
    }
}

/* #################### Dunamu Quotation 데이터를 그대로 사용 #################### */
-(void) DunamuQuotationForAllInfo {
    DunamuQuotation *usdkrwApi = [DunamuQuotation sharedInstance];
    NSString *nowSec = [[_nowTime componentsSeparatedByString:@":"] objectAtIndex:2];
    // ****** 데이터 최신화 시도 ****** //
    if (!usdkrwApi.usdkrw || [nowSec isEqual:@"05"]) {
        // 매 분의 05초에만 api 콜 진행합니다. 본 코드는 매 특정시간마다 데이터를 업데이트한다는 전제하에서 작성되었으므로, PopluarAssetListVC.m 파일에 "updateCardData"를 실행하는 주기에 따라 수정이 필요할 수 있습니다.
        [usdkrwApi fetchDataWithCompletion];
    }
    // ****** 변경된 데이터 적용 ****** //
    if (usdkrwApi.recentDateTime) {
        // 데이터 불러온 후
        if ([[[usdkrwApi.recentDateTime componentsSeparatedByString:@" "] objectAtIndex:0] isEqual:_nowDate]) {
            // ****** 평소 평일에는 환율 정보가 제공되니까 여기를 탄다 ****** //
            // 오늘 환율 정보 불러오기에 성공했을 경우, Dunamu Quotation만으로 환율 정보 제공
            // 그렇지 않은 경우에는 휴일이므로 Open Exchange Rates 정보를 제공
            // 환율
            _usdkrw = usdkrwApi.usdkrw;
            // 환율 시가
            _openUsdkrw = usdkrwApi.openUsdkrw;
            // 기준 화폐
            _paymentCurrency = usdkrwApi.paymentCurrency;
            // 최근 업데이트 날짜,시간
            _recentDateTime = usdkrwApi.recentDateTime;
            // 환율 변동가
            _changeUsdkrw = usdkrwApi.changeUsdkrw;
            // 환율 변동률
            _changePercentUsdkrw = usdkrwApi.changePercentUsdkrw;
            // 정보 제공 출처
            _provider = usdkrwApi.provider;
            // ****** ratesData 저장 = global 환율 관련 정보 ****** //
            // usd 정보
            _ratesData[@"recentUsdRates"] = usdkrwApi.ratesData[@"recentUsdRates"];
            _ratesData[@"recentOpenUsdRates"] = usdkrwApi.ratesData[@"recentOpenUsdRates"];
            _ratesData[@"recentUsdChange"] = usdkrwApi.ratesData[@"recentUsdChange"];
            _ratesData[@"recentUsdChangePercent"] = usdkrwApi.ratesData[@"recentUsdChangePercent"];
            // 기본 공통 적용 정보
            _ratesData[@"country"] = usdkrwApi.ratesData[@"country"];
            _ratesData[@"provider"] = usdkrwApi.ratesData[@"provider"];
            _ratesData[@"currencyName"] = usdkrwApi.ratesData[@"currencyName"];
            _ratesData[@"currencyUnit"] = usdkrwApi.ratesData[@"currencyUnit"];
            // krw 정보
            _ratesData[@"recentKrwRates"] = usdkrwApi.ratesData[@"recentKrwRates"];
            // KRW api에서 일단 가지고는 왔으나 비어있을 수 있음
            _ratesData[@"recentKrwChange"] = usdkrwApi.ratesData[@"recentKrwChange"];
            _ratesData[@"recentKrwChangePercent"] = usdkrwApi.ratesData[@"recentKrwChangePercent"];
            
            // 가공해서 제작
            
        } else {
            // ****** 휴일에는 환율 정보가 제공되지 않으니까 여기를 탄다 즉 평일 새벽 시간과 휴일에 환율 정보 가져오는 로직이 일치한다 ****** //
            // 환율 과거 데이터에 넣기
            // 과거 데이터 업데이트
            [self DunamuQuotationForPastInfo];
            // 현재 데이터 업데이트
            [self OpenExchangeRatesForRecentInfo];
        }
    } else {
        // ****** 데이터 불러오는 중이거나 못불러온 경우 ****** //
        NSLog(@"[INFO] Service Recent Rates : Loading Now...");
    }
}



@end

 

 

 

 

 

6. BeforeLiveTimeCalculator 파일 생성 및 소스코드 작성

 

 String 형태의 datetime과 함께 입력해주면 현재 시간으로부터 x분y초 전인지 알 수 있도록 min과 sec을 property로 가진 instance를 가집니다.

 

// vim BeforeLiveTimeCalculator.h

@interface BeforeLiveTimeCalculator : NSObject

@property (readwrite, strong, nonatomic) NSString *min;
@property (readwrite, strong, nonatomic) NSString *sec;
@property (readwrite, strong, nonatomic) NSDateFormatter *dateFormatter;

+(instancetype)sharedInstance;

-(void)WhenUpdatedMinSec: (NSString *)stringDatetime;

@end

 

// vim BeforeLiveTimeCalculator.m

#import <Foundation/Foundation.h>
#import "BeforeLiveTimeCalculator.h"

@implementation BeforeLiveTimeCalculator

+(instancetype)sharedInstance {
    
    static BeforeLiveTimeCalculator *sharedInstance = nil;
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        sharedInstance = [[self alloc] init];
        // [날짜 형식 포맷 + UTC 세팅 :: 24시간 형태]
        sharedInstance.dateFormatter = [[NSDateFormatter alloc] init];
    });
    return sharedInstance;
}

-(void)WhenUpdatedMinSec: (NSString *)stringDatetime {
    [_dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *date = [_dateFormatter dateFromString:stringDatetime];

    NSTimeInterval timeDifferenceInSeconds = [[NSDate date] timeIntervalSinceDate:date];

    int minutes = (int)(timeDifferenceInSeconds / 60);
    int seconds = (int)((int)timeDifferenceInSeconds % 60);
    _min = [NSString stringWithFormat:@"%d", minutes];
    _sec = [NSString stringWithFormat:@"%d", seconds];
}

@end

 

 

 

 

 

7. 위 소스코드 기능들을 활용해 view 그리는 GlobalRatesVC 소스코드 작성

 

// vim GlobalRatesVC.h

#import <UIKit/UIKit.h>

// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface GlobalRatesVC : UIViewController
@property (readwrite, strong, nonatomic) UIScrollView *scrollView;
@property (readwrite, strong, nonatomic) UIStackView *stackView;


/* #################### 뷰 기본 세팅 #################### */

@property (strong, nonatomic) UILabel *UILabel;

// 탑 뷰
@property (strong, nonatomic) UIView *topInfoTab;
// 라벨 : 현재 시간
@property (strong, nonatomic) UILabel *earthLabel;
@property (strong, nonatomic) UILabel *liveUTCLabel;
@property (strong, nonatomic) UILabel *koreanFlagLabel;
@property (strong, nonatomic) UILabel *liveKSTLabel;
// 라벨 : 환율
@property (strong, nonatomic) UIImageView *ratesProviderImage;
@property (strong, nonatomic) UILabel *ratesLabel;
@property (strong, nonatomic) UILabel *ratesUpdateDateTimeLabel;

// 라벨 : 카드
@property (strong, nonatomic) NSMutableArray *cardViewList; // Label

@property (strong, nonatomic) NSMutableArray *currencyCountryLabelList; // Label - 국가 오른쪽에 currency unit도 추가해 노출
@property (strong, nonatomic) NSMutableArray *currencyRatePriceLabelList; // Label
@property (strong, nonatomic) NSMutableArray *currencyRatePercentLabelList; // Label
@property (strong, nonatomic) NSMutableArray *currencyCodeLabelList; // Label

@property (strong, nonatomic) NSMutableArray *currencyKrwPriceLabelList; // Label
@property (strong, nonatomic) NSMutableArray *currencyKrwPercentLabelList; // Label

/* #################### 데이터 세팅 #################### */

// 카드 안의 정보들을 최신 데이터로 뷰 다시 그려주기
-(void) updateCardView;

// 해당 화면 전체 뷰 최신화하기
-(void) updateView;

-(void) loadGlobalRatesList;


@end

 

// vim GlobalRatesVC.m

#import <Foundation/Foundation.h>
#import "GlobalRatesVC.h"
// 수정 가능 기본 세팅값
#import "CustomizeSetting.h"
// 기본 정보 가져오기
#import "DefaultLoader.h"
// 시간 정보
#import "DateTime.h"
// x분 y초 전 정보 뽑아내기
#import "BeforeLiveTimeCalculator.h"
// 환율 정보
#import "ServiceRecentRates.h"


@implementation GlobalRatesVC {
    // json에서 가져온 paymentCurrencyList raw 데이터
    NSArray *paymentCurrencyList;
}


// **************************************** 기본 데이터 세팅하기 **************************************** //
/* #################### default.json 파일의 exchangeInfo 데이터 읽기 #################### */
-(void) loadGlobalRatesList {
    // ****** default.json의 exchangeInfo 불러오기 ****** //
    paymentCurrencyList = [DefaultLoader sharedInstance].serviceRecentRatesSetting[@"paymentCurrencyList"];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // viewDidLoad 메서드에서 스크롤 뷰를 초기화하고 설정합니다.
    // 스크롤 뷰 생성 및 초기화
    self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    self.scrollView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:self.scrollView];
    
    // 스택 뷰 생성 및 초기화
    self.stackView = [[UIStackView alloc] initWithFrame:self.scrollView.bounds];
    self.stackView.axis = UILayoutConstraintAxisVertical;
    self.stackView.distribution = UIStackViewDistributionEqualSpacing;
    self.stackView.alignment = UIStackViewAlignmentFill;
    self.stackView.spacing = 0;
    [self.scrollView addSubview:self.stackView];
    
    // default.json의 필요 데이터 가져오기
    [self loadGlobalRatesList];
    
    // 데이터를 표시할 레이블 생성
    // **************************************** [Start] 뷰 그리기 **************************************** //
    // 상단 정보칸 높이 길이 (상하 길이) 설정
    CGFloat topViewHeight = 44.0;
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 목록 상단 공백 margin 설정
    CGFloat cardViewTopStartMargin = 10.0;
    // 카드 목록의 좌우 공백 각각의 margin
    CGFloat horizontalMargin = 2.0;
    // 카드와 카드 사이의 공백
    CGFloat cardViewSpacing = 0.0;
    // 카드뷰 목록 노출 시작 x축 위치 자동 설정
    CGFloat cardViewXPosition = horizontalMargin;
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    CGFloat cardViewHeight = 22.0;
    // 카드 좌우 길이 phone size 참조하여 자동 조정
    CGFloat cardViewWidth = [UIScreen mainScreen].bounds.size.width - horizontalMargin * 2;
    // 카드뷰 안에 내용 들어가는 공간까지의 margin
    CGFloat basicMarginInCard = 4.0;
    CGFloat defaultFontSize = 16.0;
    // ****************************** //
    
    // **************************************** [Start] 최상단에 기타 정보 공간 **************************************** //
    /* #################### 현재 시간 정보 #################### */
    self.topInfoTab = [[UIView alloc] initWithFrame:CGRectMake(cardViewXPosition, cardViewTopStartMargin - (cardViewSpacing + topViewHeight), cardViewWidth, topViewHeight)];
    // 국기 너비 길이 (좌우 길이) 설정
    CGFloat miniimage = 20.0;
    
    // ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
    self.earthLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard, miniimage, 20)];
    self.earthLabel.text = @"🌍";
    // 표준 시간 = 그리니치 표준시 : 더블린, 에든버러, 리스본, 런던, 카사블랑카, 몬로비아
    self.liveUTCLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + miniimage, basicMarginInCard, cardViewWidth / 2, 20)];
    self.liveUTCLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
    
    // ****** 한국 이모티콘 및 시간 설정 ****** //
    // 태극기
    self.koreanFlagLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard + topViewHeight / 2, miniimage, 20)];
    self.koreanFlagLabel.text = @"🇰🇷";
    // 한국 시간
    self.liveKSTLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + miniimage, basicMarginInCard + topViewHeight / 2, cardViewWidth / 2, 20)];
    self.liveKSTLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
    
    /* #################### 환율 정보 #################### */
    // usdkrw 정보 출처 아이콘 이미지를 위한 기본 셋
    self.ratesProviderImage = [[UIImageView alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/12*7, basicMarginInCard, miniimage, 20)];
    self.ratesProviderImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
    // usdkrw 환율
    self.ratesLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/12*7, basicMarginInCard, cardViewWidth/8*3, 20)];
    self.ratesLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
    self.ratesLabel.textAlignment = NSTextAlignmentRight;
    // 기준 시간
    self.ratesUpdateDateTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/12*7, basicMarginInCard + topViewHeight / 2, cardViewWidth/8*3, 20)];
    self.ratesUpdateDateTimeLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
    self.ratesUpdateDateTimeLabel.textAlignment = NSTextAlignmentRight;
    
    // ****** 상단에 배치할 Label들을 topInfoTab View에 추가 ****** //
    // global 적용
    [self.topInfoTab addSubview:_earthLabel];
    [self.topInfoTab addSubview:_liveUTCLabel];
    // korea 적용
    [self.topInfoTab addSubview:_koreanFlagLabel];
    [self.topInfoTab addSubview:_liveKSTLabel];
    // 환율 적용
    [self.topInfoTab addSubview:_ratesProviderImage];
    [self.topInfoTab addSubview:_ratesLabel];
    [self.topInfoTab addSubview:_ratesUpdateDateTimeLabel];
    
    // ****** 상단 UIView를 self.scrollView에 추가 ****** //
    [self.scrollView addSubview:_topInfoTab];
    
    
    // **************************************** [Start] 카드뷰 목록 쭉 만들기 **************************************** //
    self.cardViewList = [@[] mutableCopy];
    self.currencyCountryLabelList = [@[] mutableCopy];
    self.currencyRatePriceLabelList = [@[] mutableCopy];
    self.currencyRatePercentLabelList = [@[] mutableCopy];
    self.currencyCodeLabelList = [@[] mutableCopy];
    self.currencyKrwPriceLabelList = [@[] mutableCopy];
    self.currencyKrwPercentLabelList = [@[] mutableCopy];
    
    for (int i=0; i<paymentCurrencyList.count; i++) {
        /* #################### 카드뷰 기본 세팅 #################### */
        UIView *cardView = [[UIView alloc] initWithFrame:CGRectMake(cardViewXPosition, cardViewTopStartMargin + i * (cardViewSpacing + cardViewHeight), cardViewWidth, cardViewHeight)];
        
        // UILabel의 텍스트 색상 및 배경색 설정
        // 카드뷰 배경색을 설정합니다.
        if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
            // 다크모드인 경우
            cardView.backgroundColor = [UIColor blackColor];
            [self.view addSubview:cardView];
            // 카드 테두리 다크그레이색
            cardView.layer.borderColor = [UIColor darkGrayColor].CGColor;
        } else {
            // 라이트모드인 경우
            cardView.backgroundColor = [UIColor whiteColor];
            [self.view addSubview:cardView];
            // 카드 테두리 다크그레이색
            cardView.layer.borderColor = [UIColor lightGrayColor].CGColor;
        }
        
        // 카드 테두리 두께
        cardView.layer.borderWidth = 0.5;
        
        // 카드뷰 모서리를 둥글게 설정합니다. 조건 1
        cardView.layer.cornerRadius = 0.0;
        // cardView의 경계를 기준으로 내용물이 보이는 영역을 제한합니다. masksToBounds를 YES로 설정하면, cardView의 경계 밖에 있는 모든 내용물은 자르고 숨깁니다(클립 됩니다). 즉 뷰의 경계 값을 초과한 부분을 자르기 위해 masksToBounds를 YES로 설정합니다. 반면 masksToBounds가 NO인 경우(기본값)에는 뷰의 경계 밖에 있는 내용물이 그대로 보이게 됩니다.
        cardView.layer.masksToBounds = YES;
        
        // UILabel 객체를 생성합니다. 이 레이블은 암호화폐의 이름을 표시할 것입니다.
        // 따라서 CGRect를 사용하여 레이블의 위치와 크기를 설정하며, 왼쪽 위 모서리에서 시작합니다.
        UILabel *currencyCountryLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard, cardViewWidth / 4, 20)];
        
        currencyCountryLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
//        systemFontOfSize:defaultFontSize
        // 생성한 currencyCountryLabel을 cardView의 서브뷰로 추가합니다. 이렇게 함으로써 레이블이 카드 뷰에 표시됩니다.
        [_currencyCountryLabelList addObject:currencyCountryLabel];
        [cardView addSubview:currencyCountryLabel];
        
        /* #################### 화폐별 usd rate 현재 가격, 변동률 정보 노출 라벨 세팅 #################### */
        // 환율 현재 가격
        UILabel *currencyRatePriceLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 6 * 1, basicMarginInCard, cardViewWidth / 5, 20)];
        currencyRatePriceLabel.textAlignment = NSTextAlignmentRight;
        currencyRatePriceLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyRatePriceLabel];
        [_currencyRatePriceLabelList addObject:currencyRatePriceLabel];
        
        // 환율 변동률
        UILabel *currencyRatePercentLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 42 * 16, basicMarginInCard, cardViewWidth/3, 20)];
        currencyRatePercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyRatePercentLabel];
        [_currencyRatePercentLabelList addObject:currencyRatePercentLabel];
        
        // 화폐 영어 code
        UILabel *currencyCodeLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 21 * 11, basicMarginInCard, cardViewWidth / 10, miniimage)];
        currencyCodeLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyCodeLabel];
        [_currencyCodeLabelList addObject:currencyCodeLabel];
        
        /* #################### 화폐별 krw rate 현재 가격, 변동률 정보 노출 라벨 세팅 #################### */
        // 환율 현재 가격
        UILabel *currencyKrwPriceLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 9 * 5, basicMarginInCard, cardViewWidth / 5, 20)];
        currencyKrwPriceLabel.textAlignment = NSTextAlignmentRight;
        currencyKrwPriceLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyKrwPriceLabel];
        [_currencyKrwPriceLabelList addObject:currencyKrwPriceLabel];
        
        // 환율 변동률
        UILabel *currencyKrwPercentLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 21 * 16, basicMarginInCard, cardViewWidth/7, 20)];
        currencyKrwPercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyKrwPercentLabel];
        [_currencyKrwPercentLabelList addObject:currencyKrwPercentLabel];
        
        // cardView를 self.scrollView에 추가합니다.
        [self.scrollView addSubview:cardView];
        // 레이블 세팅 완료된 cardView를 CardViewList에 넣기
        [self.cardViewList addObject: cardView];
    }
    
    // 상하 스크롤 최대치 자동 설정
    CGFloat contentHeight = paymentCurrencyList.count * (cardViewHeight + cardViewSpacing);
    self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, contentHeight);
    

    
    // NSTimer 생성 및 메서드 호출 설정 - 매 특정시간마다 호출
    [NSTimer scheduledTimerWithTimeInterval:VIEW_UPDATE_TERM
                                     target:self
                                   selector:@selector(updateCardView)
                                   userInfo:nil
                                    repeats:YES];
}

// **************************************** 카드뷰 다시 그리기 **************************************** //
-(void) updateCardView {
    // 메인 스레드에서만 UI 업데이트 수행
    dispatch_async(dispatch_get_main_queue(), ^{
        [self updateView];
    });
}

// **************************************** 전체 화면 뷰 데이터 업데이트 **************************************** //
-(void) updateView {
    /* #################### 최상단 데이터 #################### */
    // 현재 시간 확인을 위한 singlton instance 생성
    DateTime *now = [DateTime sharedInstance];
    // 레이블의 텍스트를 설정합니다. 여기에서는 UTC 시간을 업데이트합니다.
    [now NowUTC: @"yyyy-MM-dd (E) HH:mm:ss"];
    self.liveUTCLabel.text = now.dateTime;
    // 레이블의 텍스트를 설정합니다. 여기에서는 KST 시간을 업데이트합니다.
    [now NowKST: @"yyyy-MM-dd (E) HH:mm:ss"];
    self.liveKSTLabel.text = now.dateTime;
    
    // ****** USDKRW 환율 정보 업데이트 ****** //
    ServiceRecentRates *ratesLive = [ServiceRecentRates sharedInstance];
    // 환율 노출
    if (ratesLive.usdkrw) {
        // 환율 변동률 가공 및 값에 따라 환율 정보 색 지정
        NSString *ratesChangeInfo = [ratesLive.changePercentUsdkrw stringByAppendingString:@"%)"];
        if ( [ratesLive.changePercentUsdkrw hasPrefix:@"-"]) {
            // 음수인 경우 이미 - 붙어있으니까 그냥 합치기
            ratesChangeInfo = [@" (" stringByAppendingString:ratesChangeInfo];
            // 음수인 경우 하락색
            self.ratesLabel.textColor = [UIColor redColor];
        } else {
            // 양수이거나 0인 경우  + 붙여서 합치기
            ratesChangeInfo = [@" (+" stringByAppendingString:ratesChangeInfo];
            if ([ratesLive.changePercentUsdkrw isEqual:@"0.00"]) {
                // 0인 경우
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // 다크모드인 경우 글자색 흰색으로 원복
                    self.ratesLabel.textColor = [UIColor whiteColor];
                } else {
                    // 라이트모드인 경우 글자색 흑색으로 원복
                    self.ratesLabel.textColor = [UIColor blackColor];
                }
            } else {
                // 양수인 경우 상승색
                self.ratesLabel.textColor = [UIColor greenColor];
            }
        }
        // 환율 호가
        NSString *ratesMainInfo = ratesLive.usdkrw;
        self.ratesLabel.text = [ratesMainInfo stringByAppendingString:ratesChangeInfo];
        // ****** rates 정보 출처 provider 이미지 설정 ****** //
        if ([ratesLive.provider isEqual:@"하나은행"]) {
            self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].ratesApi[@"DunamuQuotation"][@"image"]];
        } else {
            self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].ratesApi[ratesLive.provider][@"image"]];
            if ( ! [ratesLive.provider isEqual:@"OpenExchangeRates"]) {
                // Error
                NSLog(@"[WARN] Rates Image Error!");
            }
        }
    } else {
        self.ratesLabel.text = @"-";
    }
    // 환율 최근 업데이트 일시가 어느 시간 전인지 노출
    BeforeLiveTimeCalculator *timeCounter = [BeforeLiveTimeCalculator sharedInstance];
    if (ratesLive.recentDateTime) {
        [timeCounter WhenUpdatedMinSec:ratesLive.recentDateTime];
        self.ratesUpdateDateTimeLabel.text = [NSString stringWithFormat:@"%@m %@s ago", timeCounter.min, timeCounter.sec];
    }
    
    /* #################### 환울 카드 데이터 = 카드뷰 기본 세팅 #################### */
    for (int i = 0; i<paymentCurrencyList.count; i++) {
        // ****** 국가명 + currency unit ****** //
        // country 데이터가 있는 경우
        UILabel *currencyCountryLabel = _currencyCountryLabelList[i];
        if (ratesLive.ratesData[@"country"][paymentCurrencyList[i]]) {
            // 국가명 옆에 currency unit 붙여주기! 단 1은 기본이니까 생략
            NSString *currencyUnit = [ratesLive.ratesData[@"currencyUnit"][paymentCurrencyList[i]] stringValue];
            if ([currencyUnit isEqual:@"1"]) {
                // 1은 기본이니 생략
                currencyCountryLabel.text = ratesLive.ratesData[@"country"][paymentCurrencyList[i]];
            } else {
                // 1이 아닌 경우에만 숫자 노출, 일반적으로 100 사용
                NSString *countryName = ratesLive.ratesData[@"country"][paymentCurrencyList[i]];
                NSString *unitInfo = [@" " stringByAppendingString:currencyUnit];
                currencyCountryLabel.text = [countryName stringByAppendingString:unitInfo];
            }
        } else {
            // country 데이터가 없는 경우
            currencyCountryLabel.text = @"";
        }
        // ****** 화폐별 1usd 기준 환율가격 ****** //
        UILabel *currencyRatePriceLabel = _currencyRatePriceLabelList[i];
        NSString *usdRate = ratesLive.ratesData[@"recentUsdRates"][paymentCurrencyList[i]];
        if (usdRate) {
            currencyRatePriceLabel.text = usdRate;
        } else {
            currencyRatePriceLabel.text = @"-";
        }
        // ****** 화폐별 1usd 기준 환율가격 변동률 ****** //
        UILabel *currencyRatePercentLabel = _currencyRatePercentLabelList[i];
        NSString *usdRateChangePercent = ratesLive.ratesData[@"recentUsdChangePercent"][paymentCurrencyList[i]];
        if (usdRateChangePercent) {
            // 값 존재 확인
            if ([usdRateChangePercent isEqual:@"0.00"]) {
                // 0.00이면 보합
                usdRateChangePercent = [@"+" stringByAppendingString: usdRateChangePercent];
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // 다크모드인 경우 글자색 흰색으로 원복
                    currencyRatePercentLabel.textColor = [UIColor whiteColor];
                } else {
                    // 라이트모드인 경우 글자색 흑색으로 원복
                    currencyRatePercentLabel.textColor = [UIColor blackColor];
                }
            } else if ([usdRateChangePercent hasPrefix:@"-"]) {
                // 음이면 하락
                currencyRatePercentLabel.textColor = [UIColor redColor];
            } else {
                // 양이면 상승
                usdRateChangePercent = [@"+" stringByAppendingString: usdRateChangePercent];
                currencyRatePercentLabel.textColor = [UIColor greenColor];
            }
            usdRateChangePercent = [usdRateChangePercent stringByAppendingString:@"%"];
            currencyRatePercentLabel.text = usdRateChangePercent;
        } else {
            // 값 없으면 빈 값으로 노출
            currencyRatePercentLabel.text = @" - %";
            if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                // 다크모드인 경우 글자색 흰색으로 원복
                currencyRatePercentLabel.textColor = [UIColor whiteColor];
            } else {
                // 라이트모드인 경우 글자색 흑색으로 원복
                currencyRatePercentLabel.textColor = [UIColor blackColor];
            }
        }
        // ****** 화폐 단위 code ****** //
        UILabel *currencyCodeLabel = _currencyCodeLabelList[i];
        currencyCodeLabel.text = paymentCurrencyList[i];
        
        // ****** 화폐별 krw 기준 1화폐당의 krw 환율 가격 ****** //
        UILabel *currencyKrwPriceLabel = _currencyKrwPriceLabelList[i];
        NSString *krwRate = ratesLive.ratesData[@"recentKrwRates"][paymentCurrencyList[i]];
        if (krwRate) {
            currencyKrwPriceLabel.text = krwRate;
        } else {
            currencyKrwPriceLabel.text = @"-";
        }
        // ****** 화폐별 krw 기준 1화폐당의 krw 환율 변동률 ****** //
        UILabel *currencyKrwPercentLabel = _currencyKrwPercentLabelList[i];
        NSString *krwRateChangePercent = ratesLive.ratesData[@"recentKrwChangePercent"][paymentCurrencyList[i]];
        if (krwRateChangePercent) {
            // 값 존재 확인
            if ([krwRateChangePercent isEqual:@"0.00"]) {
                // 0.00이면 보합
                krwRateChangePercent = [@"+" stringByAppendingString: krwRateChangePercent];
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // 다크모드인 경우 글자색 흰색으로 원복
                    currencyKrwPercentLabel.textColor = [UIColor whiteColor];
                } else {
                    // 라이트모드인 경우 글자색 흑색으로 원복
                    currencyKrwPercentLabel.textColor = [UIColor blackColor];
                }
            } else if ([krwRateChangePercent hasPrefix:@"-"]) {
                // 음이면 하락
                currencyKrwPercentLabel.textColor = [UIColor redColor];
            } else {
                // 양이면 상승
                krwRateChangePercent = [@"+" stringByAppendingString: krwRateChangePercent];
                currencyKrwPercentLabel.textColor = [UIColor greenColor];
            }
            krwRateChangePercent = [krwRateChangePercent stringByAppendingString:@"%"];
            currencyKrwPercentLabel.text = krwRateChangePercent;
        } else {
            // 값 없으면 빈 값으로 노출
            currencyKrwPercentLabel.text = @" - %";
            if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                // 다크모드인 경우 글자색 흰색으로 원복
                currencyKrwPercentLabel.textColor = [UIColor whiteColor];
            } else {
                // 라이트모드인 경우 글자색 흑색으로 원복
                currencyKrwPercentLabel.textColor = [UIColor blackColor];
            }
        }
        
    }
}


@end

 

 

 

 

 

8. MyTabBarController 소스코드 수정

 

필자의 경우 1개 탭을 추가하고, 3번째 탭에서 노출되도록 수정했습니다.

 

// vim MyTabBarController.h

#import <UIKit/UIKit.h>

// MyTabBarController라는 이름의 탭바 컨트롤러 클래스를 선언합니다.
@interface MyTabBarController : UITabBarController

@end

 

// vim MyTabBarController.m

#import <Foundation/Foundation.h>

#import "MyTabBarController.h"
// 각 메뉴탭의 첫 화면 View
#import "PopluarSymbolListVC.h"
#import "PopluarAssetListVC.h"
#import "GlobalRatesVC.h"
#import "MyProfileVC.h"

@implementation MyTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];

    // FirstViewController를 생성하고 속성을 설정합니다.
    PopluarAssetListVC *firstTabMainVC = [[PopluarAssetListVC alloc] init];
    UINavigationController *naviVC1 = [[UINavigationController alloc] initWithRootViewController:firstTabMainVC];
    naviVC1.tabBarItem.title = @"가상자산";
    naviVC1.tabBarItem.image = [UIImage systemImageNamed:@"bitcoinsign.circle"];
    //naviVC1.tabBarItem.image = [UIImage systemImageNamed:@"bitcoinsign"];
    
    // SecondViewController를 생성하고 속성을 설정합니다.
    PopluarSymbolListVC *secondTabMainVC = [[PopluarSymbolListVC alloc] init];
    UINavigationController *naviVC2 = [[UINavigationController alloc] initWithRootViewController:secondTabMainVC];
    naviVC2.tabBarItem.title = @"글로벌";
    naviVC2.tabBarItem.image = [UIImage systemImageNamed:@"globe.asia.australia"];
    
    // ThirdViewController를 생성하고 속성을 설정합니다.
    GlobalRatesVC *thirdTabMainVC = [[GlobalRatesVC alloc] init];
    UINavigationController *naviVC3 = [[UINavigationController alloc] initWithRootViewController:thirdTabMainVC];
    naviVC3.tabBarItem.title = @"환율";
    naviVC3.tabBarItem.image = [UIImage systemImageNamed:@"dollarsign.arrow.circlepath"];
    
    // ThirdViewController를 생성하고 속성을 설정합니다.
    MyProfileVC *fourthTabMainVC = [[MyProfileVC alloc] init];
    UINavigationController *naviVC4 = [[UINavigationController alloc] initWithRootViewController:fourthTabMainVC];
    naviVC4.tabBarItem.title = @"프로필";
    naviVC4.tabBarItem.image = [UIImage systemImageNamed:@"person.crop.circle"];

    // 생성된 뷰 컨트롤러를 탭바 컨트롤러에 배치합니다.
    NSArray *viewControllers = @[naviVC1, naviVC2, naviVC3, naviVC4];
    self.viewControllers = viewControllers;}

@end

 

 

 

 

 

9. 결과 예시

 

 

 

+ Recent posts