반응형

앱링크를 타임아웃을 설정하고 앱을 실행되도록 하면 
앱이 설치되어 있을 경우 앱을 실행하게된다. 

setTimeout(function () { 
	window.location = "https://itunes.apple.com/app/앱 아이디"; 
}, 25);
window.location = "앱 스킴명://";

 

iOS 14.4.1 이후 위의 타임 트릭을 사용하여 앱 호출 시

앱 실행과 스토어 이동이 중복해서 발생하는 문제가 발생된다.

이 경우 Universal Link를 사용하도록 하자.

 

2021.03.16 - [iOS/Tips] - Universal Link (1/4) - 네이티브 환경설정

2021.03.16 - [iOS/Tips] - Universal Link (2/4) - 네이티브 링크 수신

2021.03.16 - [iOS/Tips] - Universal Link (3/4) - 웹서버 환경 설정

2021.03.16 - [iOS/Tips] - Universal Link (4/4) - 웹서버 환경 검증

2020/12/08 - [프로그래밍/Java Script] - Android, iOS 앱 설치여부 체크 및 스토어 이동

2020/12/14 - [iOS/Tips] - bundle id 알아내기

2020/12/12 - [AI/Algorithm] - 2D 충돌처리

2020/12/11 - [iOS/Swift] - UIViewController 스위칭

2020/12/11 - [개발노트] - PlantUML 설치 (Mac OS X)

2020/12/11 - [개발노트] - 특수문자 발음

2020/12/10 - [iOS/Objective-C] - 웹뷰에서 javascript 함수 동기식 호출

2020/12/10 - [iOS/Tips] - Fat Static Library 빌드 (2/2)

2020/12/10 - [iOS/Tips] - Fat Static Library 빌드 (1/2)

2020/12/10 - [iOS/Tips] - Custom UserAgent 설정

2020/12/10 - [iOS/Tips] - CocoaPods 설치 및 제거

2020/12/10 - [iOS/Tips] - Clang diagnostic 경고 무시하기

2020/12/10 - [개발노트] - Bluetooth UUID

 

반응형

'개발 > iOS' 카테고리의 다른 글

WKWebView 화면 출력 완료 이벤트  (0) 2020.08.11
개발관련 폴더 경로  (0) 2020.08.06
Dictionary sort  (0) 2020.07.11
UTC시간, Locale시간 변경  (0) 2020.07.11
SEED 블록암호 알고리즘 CBC (Cipher Block Chaining) 예제 (2/2)  (0) 2020.07.11
블로그 이미지

SKY STORY

,

Dictionary sort

개발/iOS 2020. 7. 11. 21:06
반응형
반응형
블로그 이미지

SKY STORY

,
반응형
// UTC 시간을 Locale 시간으로 변환
func utcToLocale(utcDate : String, dateFormat: String) -> String
{
	let dfFormat = DateFormatter()
	dfFormat.dateFormat = dateFormat
	dfFormat.timeZone = TimeZone(abbreviation: "UTC")
	let dtUtcDate = dfFormat.date(from: utcDate)
	
	dfFormat.timeZone = TimeZone.current
	dfFormat.dateFormat = dateFormat
	return dfFormat.string(from: dtUtcDate!)
}
    
// Locale 시간을 UTC 시간으로 변환
func localeToUtc(localeDate: String, dateFormat: String) -> String
{
	let dfFormat = DateFormatter()
	dfFormat.dateFormat = dateFormat
	dfFormat.timeZone = TimeZone.current
	let dtLocaleDate = dfFormat.date(from: localeDate)
	
	dfFormat.timeZone = TimeZone(abbreviation: "UTC")
	dfFormat.dateFormat = dateFormat
	return dfFormat.string(from: dtLocaleDate!)
}
반응형
블로그 이미지

SKY STORY

,
반응형

최근 SEED 블록 암호 알고리즘 CBC 암복호화 처리를

해야할 일이 생겨 관련 클래스를 제작하게 되었습니다

혹시 같은 작업을 하시는분에게 도움이 되었으면 합니다.

아래 작업은 Swift프로젝트에서 작업되었습니다.

 

SEED 블록암호 알고리즘

seed.kisa.or.kr/kisa/Board/17/detailView.do

 

KISA 암호이용활성화 - 암호알고리즘 소스코드

한국인터넷진흥원(KISA)에서는 128비트 블록암호 SEED를 쉽게 활용할 수 있도록, ECB, CBC, CTR, CCM, GCM, CMAC 운영모드에 대한 소스코드를 배포하고 있습니다. 언어 : C/C++, Java, ASP, JSP, PHP  다음글 2019-01-3

seed.kisa.or.kr

 

KISA_SEED_CBC.h

다음 함수를 헤더에 선언해 주도록 한다.

extern int encryptSeedCBC( IN BYTE *pbszUserKey, IN BYTE *pbszIV, IN BYTE *pbszPlainText, OUT BYTE *pbszCipherText );
extern int decryptSeedCBC( IN BYTE *pbszUserKey, IN BYTE *pbszIV, IN BYTE *pbszCipherText, OUT BYTE *pbszPlainText );

 

KISA_SEED_CBC.c

다음과 같이 작성해 준다.

작성에 앞서 main()함수는 제거해주도록 하자.

int encryptSeedCBC( IN BYTE *pbszUserKey, IN BYTE *pbszIV, IN BYTE *pbszPlainText, OUT BYTE *pbszCipherText )
{
    printf("\n---------------------------------");
    printf("\nplainText : %s\n", (char *)pbszPlainText);
    
    int nPlainTextLen = (int)strlen((char *)pbszPlainText);// 평문의 Byte길이
    printf ("\n---------------------------------");
    printf ("\nSEED CBC Encryption....\n");
    // 암호문의 Byte길이 - 패딩 로직때문에 16바이트 블럭으로 처리함으로 pbszCipherText는 평문보다 16바이트 커야 한다.
    int nCipherTextLen = SEED_CBC_Encrypt( pbszUserKey, pbszIV, pbszPlainText, nPlainTextLen, pbszCipherText );
    return nCipherTextLen;
}

int decryptSeedCBC( IN BYTE *pbszUserKey, IN BYTE *pbszIV, IN BYTE *pbszCipherText, OUT BYTE *pbszPlainText )
{
    int nCipherTextLen = (int)strlen((char *)pbszCipherText);// 암호문의 Byte길이
    printf ("\n---------------------------------");
    printf ("\nSEED CBC Decryption....\n");
    // 평문의 Byte길이
    int nPlainTextLen = SEED_CBC_Decrypt( pbszUserKey, pbszIV, pbszCipherText, nCipherTextLen, pbszPlainText );
    return nPlainTextLen;
}

 

CryptoUtil  유틸 클래스 생성

//
//  CryptoUtil.h
//  
//
//  Created by netcanis on 2020/07/09.
//  Copyright © 2020 netcanis. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface CryptoUtil : NSObject

+(NSString *)encrypt:(NSString *)masterKey msg:(NSString *)msg;
+(NSString *)encrypt:(Byte *)key iv:(Byte *)iv msg:(NSString *)msg;

+(NSString *)decrypt:(NSString *)masterKey msg:(NSString *)base64Str;
+(NSString *)decrypt:(Byte *)key iv:(Byte *)iv msg:(NSString *)base64Str;

+(NSData *)getSHA256:(NSString *)msg;
+(NSString *)hash:(NSString *)secret payload:(NSString *)payload;

@end

NS_ASSUME_NONNULL_END

 

//
//  CryptoUtil.m
//  
//
//  Created by netcanis on 2020/07/09.
//  Copyright © 2020 netcanis. All rights reserved.
//

#import "CryptoUtil.h"
#import "CocoaSecurity.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "KISA_SEED_CBC.h"


@implementation CryptoUtil



+(NSString *)encrypt:(NSString *)masterKey msg:(NSString *)msg {
    NSData *sha256 = [self getSHA256:masterKey];
    NSData *key = [sha256 subdataWithRange:NSMakeRange(0, 16)];
    NSData *iv = [sha256 subdataWithRange:NSMakeRange(16, 16)];
    
    BYTE pbszPlainText[2048] = {0,};
    memset(pbszPlainText, 0, [msg length]);
    const char *byteBuff = [msg UTF8String];
    memcpy(pbszPlainText, (BYTE *)byteBuff, strlen(byteBuff));
    
    // 암호문 출력 버퍼
    BYTE pbszCipherText[2048] = {0,};
    int nCipherTextLen = encryptSeedCBC( (BYTE *)[key bytes], (BYTE *)[iv bytes], pbszPlainText, pbszCipherText );

    // base64 encode
    NSData *data = [[NSData alloc] initWithBytes:pbszCipherText length:nCipherTextLen];
    NSString *base64String = [data base64EncodedStringWithOptions:0];

    // memory clean
    memset(pbszPlainText, 0, nCipherTextLen);
    memset((void *)[data bytes], 0, [data length]);

#ifdef DEBUG
    // log
    [CryptoUtil printHexaLog:pbszCipherText length:nCipherTextLen];
#endif//DEBUG
    
    return base64String;
}

+(NSString *)encrypt:(Byte *)key iv:(Byte *)iv msg:(NSString *)msg {
    
    BYTE pbszPlainText[2048] = {0,};
    memset(pbszPlainText, 0, [msg length]);
    const char *byteBuff = [msg UTF8String];
    memcpy(pbszPlainText, (BYTE *)byteBuff, strlen(byteBuff));
    
    // 암호문 출력 버퍼
    BYTE pbszCipherText[2048] = {0,};
    int nCipherTextLen = encryptSeedCBC( key, iv, pbszPlainText, pbszCipherText );

    // base64 encode
    NSData *data = [[NSData alloc] initWithBytes:pbszCipherText length:nCipherTextLen];
    NSString *base64String = [data base64EncodedStringWithOptions:0];

    // memory clean
    memset(pbszPlainText, 0, nCipherTextLen);
    memset((void *)[data bytes], 0, [data length]);
    
#ifdef DEBUG
    // log
    [CryptoUtil printHexaLog:pbszCipherText length:nCipherTextLen];
#endif//DEBUG
    
    return base64String;
}

+(NSString *)decrypt:(NSString *)masterKey msg:(NSString *)base64Str {
    NSData *sha256 = [self getSHA256:masterKey];
    NSData *key = [sha256 subdataWithRange:NSMakeRange(0, 16)];
    NSData *iv = [sha256 subdataWithRange:NSMakeRange(16, 16)];
    
    // base64 decode
    NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Str options:0];

    BYTE pbszCipherText[2048] = {0,};
    memcpy(pbszCipherText, [data bytes], [data length]);

    // 사용자 입력 평문
    BYTE pbszPlainText[2048] = {0,};
    int nPlainTextLen = decryptSeedCBC( (BYTE *)[key bytes], (BYTE *)[iv bytes], pbszCipherText, pbszPlainText );

    NSString *plainText = [[NSString alloc] initWithBytes:pbszPlainText length:nPlainTextLen encoding:NSUTF8StringEncoding];

    // memory clean
    memset(pbszCipherText, 0, nPlainTextLen);

#ifdef DEBUG
    // log
    [CryptoUtil printHexaLog:pbszPlainText length:nPlainTextLen];
#endif//DEBUG
    
    return plainText;
}

+(NSString *)decrypt:(Byte *)key iv:(Byte *)iv msg:(NSString *)base64Str {
    
    // base64 decode
    NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Str options:0];

    BYTE pbszCipherText[2048] = {0,};
    memcpy(pbszCipherText, [data bytes], [data length]);

    // 사용자 입력 평문
    BYTE pbszPlainText[2048] = {0,};
    int nPlainTextLen = decryptSeedCBC( key, iv, pbszCipherText, pbszPlainText );

    NSString *plainText = [[NSString alloc] initWithBytes:pbszPlainText length:nPlainTextLen encoding:NSUTF8StringEncoding];

    // memory clean
    memset(pbszCipherText, 0, nPlainTextLen);
    
#ifdef DEBUG
    // log
    [CryptoUtil printHexaLog:pbszPlainText length:nPlainTextLen];
#endif//DEBUG
    
    return plainText;
}

+(NSData *)getSHA256:(NSString *)msg {
    NSData *data = [CocoaSecurity sha256:msg].data;
    return data;
}

+(NSString *)hash:(NSString *)secret payload:(NSString *)payload {
    NSString *result = [CocoaSecurity hmacSha256:payload hmacKey:secret].base64;
    return result;
}


+(void)printHexaLog:(BYTE *)pbBytes length:(NSInteger)length {
    printf ("\nLength (%ld)  : ", (long)length);
    for (int i=0;i<length;i++) {
        printf("%02X ",pbBytes[i]);
    }
}

@end

 

암호화 처리를 위해 CocoaSecurity 클래스를 사용했다.

https://github.com/kelp404/CocoaSecurity/tree/master/CocoaSecurity

 

kelp404/CocoaSecurity

Encrypt/Decrypt: AES. Hash: MD5, SHA(SHA1, SHA224, SHA256, SHA384, SHA512). Encode/Decode: Base64, Hex. - kelp404/CocoaSecurity

github.com

 

해당 클래스도 프로젝트에 추가해 준다.

 

[ 사용방법 ] 

// encode
let msg = "2020070917085299920190520000001abcdefghijklmn"
let masterKey = "1234567890";
let enc = CryptoUtil.encrypt(masterKey, msg: msg)
print("\(enc)")

// output :
3JomiSXGgIuC6vbFELbIa3+5QyJlpv6cFgy6JDcPmRryk2V+y2sueCivAymkra6D


// decode
let dec = CryptoUtil.decrypt(masterKey, msg: enc)

// output :
2020070917085299920190520000001abcdefghijklmn

 

 

2020/07/11 - [분류 전체보기] - SEED 블록암호 알고리즘 CBC (Cipher Block Chaining) 예제2

2020/07/11 - [Android/Java] - Webview에서 새로운창 출력

2020/07/11 - [Android/Tips] - Could not find com.android.tools.build:aapt2:4.0.0-6051327.

2020/07/11 - [Android/Kotlin] - byte array to hex string

2020/07/11 - [Android/Tips] - Android API Level 및 명칭

2020/07/11 - [Android/Tips] - ERROR: Could not get unknown property 'com' for root project 'myApp' of type org.gradle.api.Project

2020/07/11 - [Android/Tips] - error: cannot find symbol this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);

2020/07/11 - [Android/Rooting] - LG 펌웨어 추출 및 OS 다운그레이드 (kdz file)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 환경설정 (4/4)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 환경설정 (3/4)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 환경설정 (2/4)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 환경설정 (1/4)

2020/06/16 - [OS/Mac OS X] - duplicate symbol 에러 해결

2020/06/12 - [iOS/Jailbreak] - Fridump 사용법 (4/4) - 결과물 바이너리 검색

2020/06/03 - [iOS/Objective-C] - NSString <-> CBUUID

반응형

'개발 > iOS' 카테고리의 다른 글

Dictionary sort  (0) 2020.07.11
UTC시간, Locale시간 변경  (0) 2020.07.11
Fridump 사용법 (4/4) - 결과물 바이너리 검색  (0) 2020.06.12
NSString <-> CBUUID  (0) 2020.06.03
위치서비스(location service) 활성화 여부 체크  (0) 2020.06.03
블로그 이미지

SKY STORY

,
반응형

메모리 덤프 완료 후 결과물에서 strings.txt파일만 열어서

검출 여부를 판단해서는 안된다.

이 경우 스트링 버퍼를 사용한 경우 대부분 검출이 되지만 만약

바이너리 상태에서 문자열을 처리한 경우는 검출되지 않는다.

위 사진은 메모리 덤프 후 나온 결과물이다. 

바이너리 상태의 문자열을 찾아보려면 위에 data파일들을 모두 

찾아봐야한다. 

맥에서 사용할 만한 바이너리 에디터로 data파일을 열어보았다.

사용한 앱은  0xED 이다. 

다운로드 : www.macupdate.com/app/mac/22750/0xed

 

원한는 값을 Hex 또는 Text로 검색

덤프 완료후 100개가 넘는 바이너리 덤프파일들이 생성되므로 

폴더내 모든 파일들을 열어서 바이너리를 검색할 수 있는 툴(Ultraedit)을 사용해야한다.

다운로드 : www.ultraedit.com/downloads/

 'Find In Files...'를 선택한다.

검색할 폴더를 선택하고 검색하면 폴더내 모든 바이너리 덤프파일들을 검색하여 결과를 출력해 준다.

 

결과내용을 클릭하면 해당 파일에 대한 내용을 확인할 수 있다.

 

2020/05/19 - [iOS/Jailbreak] - Fridump 사용법 (1/4) - iOS디바이스 환경 구축

2020/05/19 - [iOS/Jailbreak] - Fridump 사용법 (2/4) - Mac OS X 환경 구축

2020/05/19 - [iOS/Jailbreak] - Fridump 사용법 (3/4) - 메모리 덤프

2020/06/12 - [iOS/Jailbreak] - Fridump 사용법 (4/4) - 결과물 바이너리 검색

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 사용하기 (1/4)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 사용하기 (2/4)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 사용하기 (3/4)

2020/07/11 - [Android/Rooting] - 안드로이드 Fridump 사용하기 (4/4)

2020/06/03 - [iOS/Objective-C] - NSString <-> CBUUID

2020/06/03 - [iOS/Swift] - 위치서비스(location service) 활성화 여부 체크

2020/06/02 - [개발노트] - Luhn 알고리즘

2020/06/01 - [iOS/Swift] - The Ultimate Guide to WKWebView

2020/06/01 - [iOS/Tips] - WKWebView에서 로컬 웹 파일 및 리소스 로드

2020/06/01 - [iOS/Tips] - WKWebView

2020/05/29 - [iOS/Swift] - 네비게이션바 투명 처리

2020/05/29 - [개발툴/Xcode] - NFC Xcode 설정

2020/05/29 - [개발노트] - NFC (Near Field Communication)

2020/05/29 - [개발노트] - NFC Tag 저장용량

2020/05/29 - [개발노트] - NDEF

2020/05/29 - [개발노트] - Mifare

2020/05/29 - [iOS/Swift] - 클로저(Closure)

2020/05/29 - [개발노트] - QR 코드 결제 타입

2020/05/29 - [iOS/Objective-C] - Objective-C Block Syntax

반응형
블로그 이미지

SKY STORY

,

NSString <-> CBUUID

개발/iOS 2020. 6. 3. 15:54
반응형

특정 문자열을 UUID 포멧 문자열로 바꿔야하는 경우가 있다. 이런경우 NSString과 CBUUID간 변환이 용이해야 한다. 만약 특정 문자열이 길이가 uuid포멧 길이보다 작다면 uuid포멧 빈 공간에 ' '을 채워서 생성되도록 만들어 보았다.

 

NSString -> CBUUID

NSString *string = [@"233R194013780790" toUUID];
CBUUID *uuid = [CBUUID UUIDWithString:string];

 

CBUUID -> NSString

// 32333352-3139-3430-3133-373830373930
NSString *uuidString = [uuid representativeString];
NSLog(@"%@", uuidString);

// 233R194013780790
NSString *tid = [uuid toString];
NSLog(@"%@", tid);

 

 

::: 관련함수 :::

// String to Hexadecimal String
-(NSString *)toHex {
    char *utf8 = (char *)[self UTF8String];
    NSMutableString *hex = [NSMutableString string];
    while ( *utf8 ) {
        [hex appendFormat:@"%02X" , *utf8++ & 0x00FF];
    }
    return [NSString stringWithFormat:@"%@", hex];
}

// Hexadecimal String to NSData
-(NSData *)hex2Data {
    NSMutableData *stringData = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [self length] / 2; i++) {
        byte_chars[0] = [self characterAtIndex:i*2];
        byte_chars[1] = [self characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [stringData appendBytes:&whole_byte length:1];
    }
    return stringData;
}

-(CBUUID *)toCBUUID {
    if (NO == [self isValidUUID]) {
        NSLog(@"포멧 비정상.");
        return nil;
    }
    return [CBUUID UUIDWithString:self];
}

// uuid 포멧 검증
-(BOOL)isValidUUID {
    return (BOOL)[[NSUUID alloc] initWithUUIDString:self];
}

// 문자열을 hex로 변경한뒤 UUID포멧으로 변경
-(NSString *)toUUID {
    NSString *string = self;
    if (string.length < 16) {
        NSString *spaceString = @"";
        for (NSInteger i=0; i<(16 - string.length); ++i) {
            spaceString = [spaceString stringByAppendingString:@" "];
        }
        // 길이가 작을 경우 뒤쪽에 공백추가
        string = [NSString stringWithFormat:@"%@%@", self, spaceString];
    }

    NSData *data = [string toData];
    NSUInteger bytesToConvert = [data length];
    const unsigned char *uuidBytes = [data bytes];
    NSMutableString *outputString = [NSMutableString stringWithCapacity:16];

    for (NSUInteger currentByteIndex = 0; currentByteIndex < bytesToConvert; currentByteIndex++) {
        switch (currentByteIndex) {
            case 3:
            case 5:
            case 7:
            case 9:[outputString appendFormat:@"%02X-", uuidBytes[currentByteIndex]]; break;
            default:[outputString appendFormat:@"%02X", uuidBytes[currentByteIndex]];
        }
    }
    return outputString;
}

-(NSData *)toData  {
    NSData *nsData = [self dataUsingEncoding:NSUTF8StringEncoding];
    return nsData;
}

 

2020/06/03 - [iOS/Swift] - 위치서비스(location service) 활성화 여부 체크

2020/06/02 - [개발노트] - Luhn 알고리즘

2020/06/01 - [iOS/Swift] - The Ultimate Guide to WKWebView

2020/06/01 - [iOS/Tips] - WKWebView에서 로컬 웹 파일 및 리소스 로드

2020/06/01 - [iOS/Tips] - WKWebView

2020/05/29 - [iOS/Swift] - 네비게이션바 투명 처리

2020/05/29 - [개발툴/Xcode] - NFC Xcode 설정

2020/05/29 - [개발노트] - NFC (Near Field Communication)

2020/05/29 - [개발노트] - NFC Tag 저장용량

2020/05/29 - [개발노트] - NDEF

2020/05/29 - [개발노트] - Mifare

2020/05/29 - [iOS/Swift] - 클로저(Closure)

2020/05/29 - [개발노트] - QR 코드 결제 타입

2020/05/29 - [iOS/Objective-C] - Objective-C Block Syntax

2020/05/29 - [iOS/Objective-C] - Detect permission of camera in iOS

 

반응형
블로그 이미지

SKY STORY

,
반응형
import CoreLocation

...

if CLLocationManager.locationServicesEnabled() {
    switch CLLocationManager.authorizationStatus() {
        case .notDetermined, .restricted, .denied:
            print("접근불가 - 앱 실행 시 또는 앱 설정에서 위치서비스를 사용을 허가하지 않은 경우")
        case .authorizedAlways, .authorizedWhenInUse:
            print("접근허용 - 앱의 위치서비스 사용을 허가한 경우")
        @unknown default:
            break
    }
} else {
	print("비활성화 - 설정/개인 정보 보호/위치 서비스를 비활성화한 경우")
}

 

 

2020/06/02 - [개발노트] - Luhn 알고리즘

2020/06/01 - [iOS/Swift] - The Ultimate Guide to WKWebView

2020/06/01 - [iOS/Tips] - WKWebView에서 로컬 웹 파일 및 리소스 로드

2020/06/01 - [iOS/Tips] - WKWebView

2020/05/29 - [iOS/Swift] - 네비게이션바 투명 처리

2020/05/29 - [개발툴/Xcode] - NFC Xcode 설정

2020/05/29 - [개발노트] - NFC (Near Field Communication)

2020/05/29 - [개발노트] - NFC Tag 저장용량

2020/05/29 - [개발노트] - NDEF

2020/05/29 - [개발노트] - Mifare

2020/05/29 - [iOS/Swift] - 클로저(Closure)

2020/05/29 - [개발노트] - QR 코드 결제 타입

2020/05/29 - [iOS/Objective-C] - Objective-C Block Syntax

2020/05/29 - [iOS/Objective-C] - Detect permission of camera in iOS

반응형

'개발 > iOS' 카테고리의 다른 글

Fridump 사용법 (4/4) - 결과물 바이너리 검색  (0) 2020.06.12
NSString <-> CBUUID  (0) 2020.06.03
The Ultimate Guide to WKWebView  (0) 2020.06.01
WKWebView에서 로컬 웹 파일 및 리소스 로드  (0) 2020.06.01
WKWebView  (0) 2020.06.01
블로그 이미지

SKY STORY

,
반응형

WKWebView is a powerhouse on iOS, providing high-performance web rendering wherever and whenever you need.

In this article I’ve put together 15 of the most common use cases for WKWebView, and provided hands-on code solutions for each of them. So, if you want to solve a specific problem, or if you just want to see what WebKit is capable of, read on!

SPONSORED Are you tired of wasting time debugging your Swift app?Instabug’s SDK is here to help you minimize debugging time by providing you with complete device details, network logs, and reproduction steps with every bug report. All data is attached automatically, and it only takes a line of code to setup. Start your free trial now and get 3 months off exclusively for the Hacking with Swift Community.

1. Making a web view fill the screen

Sometimes you’ll see folks add code to viewDidLoad() to create a web view then make it fill all available space. This is inefficient, and also far harder than it needs to be.

A simpler approach is to add a property to your view controller like this:

let webView = WKWebView()

Then overriding the loadView() method to assign that to your view controller’s view, like this:

override func loadView() { self.view = webView }

Having a dedicated webView property is helpful so that you can reference its properties and methods more easily later on.

2. Loading remote content

Given that one of the major uses for WKWebView is to load remote content, it’s weirdly not just one line of code. Instead, you create a URL from a string, wrap that in a URLRequest, then ask the web view to load that:

if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) }

If you intend to load URLs frequently, you might find it easier to wrap that behavior inside an extension:

extension WKWebView { func load(_ urlString: String) { if let url = URL(string: urlString) { let request = URLRequest(url: url) load(request) } } }

Now you can load a website just by running webView.load("https://www.apple.com").

3. Loading local content

WKWebView can load any HTML stored in your app bundle using its loadFileURL()method. You should provide this with a URL to some HTML file that you know is in your bundle, along with another URL that stores any other files you want to allow the web view to read.

For example, if you wanted to load a file called "help.html" you might use code like this:

if let url = Bundle.main.url(forResource: "help", withExtension: "html") { webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent()) }

That url.deletingLastPathComponent() part tells WebKit it can read from the directory that contains help.html – that’s a good place to put any assets such as images, JavaScript, or CSS.

4. Loading HTML fragments

You can generate HTML in code and feed that directly to WKWebView. For example, this displays a heading message:

let html = """ <html> <body> <h1>Hello, Swift!</h1> </body> </html> """ webView.loadHTMLString(html, baseURL: nil)

Note the baseURL parameter to loadHTMLString(). If you are referencing assets in your bundle such as images or CSS, you should specify Bundle.main.resourceURL there so they can be loaded. For example:

webView.loadHTMLString(html, baseURL: Bundle.main.resourceURL)

5. Controlling which sites can be visited

By default WKWebView allows access to all available websites, but it’s easy to lock down the list to any criteria of your choosing.

First, make something conform to WKNavigationDelegate – it could be your view controller, but it doesn’t have to be. For example:

class ViewController: UIViewController, WKNavigationDelegate {

Second, make that object the navigation delegate of your web view. If you were using your view controller, you’d write this:

webView.navigationDelegate = self

Finally, implement the decidePolicyFor method, adding any logic you want to decide whether the page should be loaded. As an example, this implementation allows the user to visit the Apple homepage and nothing else:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if let host = navigationAction.request.url?.host { if host == "www.apple.com" { decisionHandler(.allow) return } } decisionHandler(.cancel) }

6. Opening a link in the external browser

It’s common to want to handle some links inside your app and others externally, and this doesn’t take much work thanks to the WKNavigationDelegate protocol.

First, make an object conform to the protocol – it might be your view controller, but it doesn’t need to be. For example:

class ViewController: UIViewController, WKNavigationDelegate {

Second, set that object as your web view’s navigation delegate. If you were using your view controller you would write this:

webView.navigationDelegate = self

Finally, implement the decidePolicyFor method with whatever logic should decide whether the page is loaded internally or externally. For internal loads make sure you call the decisionHandler() closure with .cancel so the load halts, while also calling UIApplication.shared.open() to have the URL open in the external browser.

As an example, this implementation will load all links inside the web view as long as they don’t go to the Apple homepage:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if let url = navigationAction.request.url { if url.host == "www.apple.com" { UIApplication.shared.open(url) decisionHandler(.cancel) return } } decisionHandler(.allow) }

7. Monitoring page loads

Loading one web page means fetching some HTML, downloading any JavaScript and CSS it uses, downloading any images, and so on.

To help keep your users informed, it’s a good idea to monitor page loads and show some sort of user interface updates so they know something is happening. This can be done by observing the estimatedProgress property, like this:

webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)

You should now implement the observeValue(forKeyPath:) method, which will pass you a string saying what changed. If that’s set to “estimatedProgress” then we can do something interesting with the web view’s latest estimatedProgressproperty:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "estimatedProgress" { print(Float(webView.estimatedProgress)) } }

8. Reading a web page’s title as it changes

You can use webView.title to read the current page title, but because page titles change as the user browses around you’re likely to want to be notified whenever it changes. 

To do that, first register to receive notifications when the title changes:

webView.addObserver(self, forKeyPath: #keyPath(WKWebView.title), options: .new, context: nil)

Now implement the observeValue(forKeyPath:) method. This will pass you a string saying what changed, and if that’s “title” then you can do something with it.

To get you started, this code just prints the title whenever it changes:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "title" { if let title = webView.title { print(title) } } }

9. Reading pages the user has visited

If you’re serious about building a useful browser experience, you’ll probably want to read the list of sites the user has visited. This is all available inside the backForwardList property of web views, which itself contains the array backListand forwardList.

Inside each of those arrays you can read the URL for each page that was visited, along with the title that was used. For example, you can print out a list of all sites the user has already visited using this loop:

for page in webView.backForwardList.backList { print("User visited \(page.url.absoluteString)") }

10. Injecting JavaScript into a page

Once your web view has loaded some content, you can execute any JavaScript you like inside that rendered page by using the evaluateJavaScript() method. All you need to do is give it some JavaScript to execute – reading some value, for example – along with a closure to run when execution has finished.

As an example, if you had a page that contained <div id="username">@twostraws</div> and you wanted to read the “@twostraws” part, you would use this:

webView.evaluateJavaScript("document.getElementById('username').innerText") { (result, error) in if let result = result { print(result) } }

11. Reading and deleting cookies

You can read through the complete list of cookies associated with a website by using the httpCookieStore property of your web view. This is buried under the configuration.websiteDataStore property, but once you find it you can call getAllCookies() to get an array of cookies, or call delete() to delete a specific cookie.

As an example, this code loops over all cookies, and when it finds one called “authentication” deletes it – all other cookies are just printed out:

webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in for cookie in cookies { if cookie.name == "authentication" { self.webView.configuration.websiteDataStore.httpCookieStore.delete(cookie) } else { print("\(cookie.name) is set to \(cookie.value)") } } }

12. Providing a custom user agent

User agents let your web servers identify the type of web browser that is accessing a page, and are commonly used to enable or limit which features are available.

If you’re reading pages from your own server, you can adjust the user agent to your own string so that you can identify users of your app. For example:

webView.customUserAgent = "My Awesome App"

Note: There's nothing stopping you from changing the user agent when accessing other resources, but keep in mind that some sites might read the user agent string and get confused if it doesn't match what they expect.

13. Showing custom UI

WKWebView is a bit like having one lone tab in the iOS Safari app, which means the user can’t open and close new windows to browse multiple pages, and it won’t even show alerts or confirmation requests triggered by JavaScript.

Fortunately, you can change that using the WKUIDelegate protocol: set an object to be the UI delegate of your web view and you can show custom alerts, manage your own tabs, and more.

First, make some object such as your view controller conform to it:

class ViewController: UIViewController, WKUIDelegate {

Second, assign your view controller to the uiDelegate property of your web view:

webView.uiDelegate = self

Finally, implement as many of the optional methods of WKUIDelegate as you want. For example, you can make WKWebView show a custom alert controller when any web page uses the alert() JavaScript function:

func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { let ac = UIAlertController(title: "Hey, listen!", message: message, preferredStyle: .alert) ac.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) present(ac, animated: true) completionHandler() }

There’s also runJavaScriptConfirmPanelWithMessage for showing confirm and deny UI, runJavaScriptTextInputPanelWithPrompt for requesting user text input, and so on.

Note: You must call the completion handler when you’re finished. JavaScript’s alerts are blocking, which means JavaScript execution will not continue until the alert finishes. As a result, WebKit will complain if you don’t let it know when you’re done.

14. Snapshot part of the page

Although you can use the regular drawHierarchy() method for converting a view to an image, WebKit comes with its own takeSnapshot() method that lets you crop and size the image as needed.

For example, this will produce a 150x50 image from the top-left of the web view:

let config = WKSnapshotConfiguration() config.rect = CGRect(x: 0, y: 0, width: 150, height: 50) webView.takeSnapshot(with: config) { image, error in if let image = image { print(image.size) } }

If you don’t want a cropped image - i.e. you want the whole thing – just use nil instead of config.

15. Detecting data

Web views have built-in data detector support, which means they can make things like phone numbers, calendar events, and flight numbers into tappable links.

These are all disabled so the default behavior is to render web pages as they were designed, but it’s trivial to override – just create your web view using a custom WKWebViewConfiguration object.

For example, this instructs the web view to detect all possible types of data:

let config = WKWebViewConfiguration() config.dataDetectorTypes = [.all] let webView = WKWebView(frame: .zero, configuration: config)

출처 : www.hackingwithswift.com/articles/112/the-ultimate-guide-to-wkwebview

반응형

'개발 > iOS' 카테고리의 다른 글

NSString <-> CBUUID  (0) 2020.06.03
위치서비스(location service) 활성화 여부 체크  (0) 2020.06.03
WKWebView에서 로컬 웹 파일 및 리소스 로드  (0) 2020.06.01
WKWebView  (0) 2020.06.01
네비게이션바 투명 처리  (0) 2020.05.29
블로그 이미지

SKY STORY

,