1.Build Phases -> Link Binary With Libraries 에서

CoreSpotlight.framework 추가하기.


2. 설명이미지



3. 코드 작성

#import <CoreSpotlight/CoreSpotlight.h>

#import <MobileCoreServices/MobileCoreServices.h>


-(void)addSportlightSearchItem:(NSString*)title desc:(NSString*)desc keywords:(NSArray*)keywords isHaveImageData:(BOOL)imageDataAvailable imageData:(NSData*)imageData {

    CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString*)kUTTypeImage];

    attributeSet.title = title;

    attributeSet.contentDescription = desc;

    attributeSet.keywords = keywords;

    if(imageDataAvailable) {

        attributeSet.thumbnailData = imageData;

    }else {

        attributeSet.thumbnailData = NULL;

    }


    CSSearchableItem *item = [[CSSearchableItem alloc] initWithUniqueIdentifier:desc domainIdentifier:desc attributeSet:attributeSet];

    [[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:@[item] completionHandler:^(NSError * __nullable error) {

        if(!error) {

            NSLog(@"등록완료");

        }

    }];

}




WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret






1.Build Phases -> Link Binary With Libraries 에서

Contacts.framework 추가하기.


2. 코드 작성

-(void)loadContactList {

    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];

    if( status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusRestricted)

    {

        NSLog(@"access denied");

    }

    else

    {

        //Create repository objects contacts

        CNContactStore *contactStore = [[CNContactStore alloc] init];

        

        //Select the contact you want to import the key attribute  ( https://developer.apple.com/library/watchos/documentation/Contacts/Reference/CNContact_Class/index.html#//apple_ref/doc/constant_group/Metadata_Keys )

        

        NSArray *keys = [[NSArray alloc]initWithObjects:CNContactGivenNameKey,CNContactFamilyNameKey,CNContactPhoneNumbersKey,CNContactImageDataKey, CNContactImageDataAvailableKey, nil];

        

        // Create a request object

        CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];

        request.predicate = nil;

        

        NSMutableArray  *contactArray = [[NSMutableArray alloc] init];

        [contactStore enumerateContactsWithFetchRequest:request

                                                  error:nil

                                             usingBlock:^(CNContact* __nonnull contact, BOOL* __nonnull stop)

         {

             // Contact one each function block is executed whenever you get

             NSString *phoneNumber = @"";

             if( contact.phoneNumbers)

                 phoneNumber = [[[contact.phoneNumbers firstObject] value] stringValue];

             

             NSLog(@"phoneNumber = %@", phoneNumber);

             NSLog(@"givenname = %@", contact.givenName);

             NSLog(@"familyname = %@", contact.familyName);

             

             [contactArray addObject:contact];

         }];

    }

}




WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret





Object-C Style

CGFloat red, green, blue, alpha;

red = 1.0f;

CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, alpha);


//or

CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor redColor] CGColor]);


Swift Style

let myUIColor = UIColor.purpleColor()

var r:CGFloat, g:CGFloat, b:CGFloat, a:CGFloat = 0

myUIColor.getRed(&r, green: &g, blue: &b, alpha: &a)

CGContextSetRGBStrokeColor(c, r, g, b, a)


// or

CGContextSetStrokeColorWithColor(myUIColor.CGColor);



WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret





단순히 모바일웹만 보여주는 앱은 리젝대상입니다.
꼭 앱으로 만들어야 하는 명확한 이유가 있어야 합니다. 


Apple의 리뷰 가이드라인이 있지만 하이브리드 앱, 웹앱에 대해서 명확한 규정은 있지 않기 때문에 Apple의 리뷰어가 누가 되는지에 따라서도 많이 달라집니다.
하지만 사람들의 경험으로 웹앱이 통과하기 위해 몇가지 암묵적인 규칙이 있다고 합니다.


1. 푸쉬를 써야한다(푸쉬 뿐만아니라, 연락처, 위치정보 등 앱으로만 수행할 수 있는 기능이 꼭 들어가야 합니다.)
2. 네트워크 연결이 되지 않은 상태에서 실행했을때도 뭔가가 있어야 한다
     - 처음시작할때 인트로가 필요할테고, 네트워크 접속 오류 메세지 화면이 꼭 필요합니다.
3. 웹으로는 회원가입이 불가능하게 해야한다.
     - 꼭 애플의 앱에서 회원가입이 가능하게 해야합니다. 
     - 기획자들이 웹의 PV를 높이기 위해 웹에서만 가입하게 한 서비스가 리젝 당한 경우가 있었습니다.


이 세가지 내용을 충족시켜준다면 무리없이 앱스토어에 등록이 될것으로 예상됩니다.
하지만 애플의 리뷰는 사람이 하니깐, 리뷰어에게 직접 메일이나 전화를 통해 위의 기능이 빠지더라도 꼭 애플의 앱스토어에 등록 되어야 할 이유를 설명하면 통과될 가능성도 있습니다.



WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret





iOS9으로 업데이트 되면서, HTTP로 접속을 하거나, 인증되지 않은 HTTPS

즉, 정상적인 SSL이 아닌 곳으로 이동이나 webView를 띄우면 아래와 같은 에러가 나게 됩니다.

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)


자세한 내용은 아래에 링크에서 확인하면 됩니다. 아래는 WWDC 2015 발표되었던 ATS에 관해 잘 설명하고 있는 링크입니다.


http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/



App Transport Security에 대해 자세히 설명해보겠습니다.



App Transport Security는?


App Transport Security(이후 ATS)는 iOS 9.0또는 OS X 10.11 이상 유효하며, 응용프로그램과 웹 서비스간의 안전한 연결을 위해 사용할 수 있습니다.


ATS가 활성화되면 HTTP를 통해 통신을 할 수 없습니다. 또한 Apple에서 권장하는 요구 사항을 충족하지 않는 연결은 강제로 연결 실패 처리됩니다. 예를 들어, Apple 권장 요구 사항을 충족하지 않는 Web 페이지를 WKWebView 에서 열려고 하면 페이지로드는 실패합니다. 그 때의 NSError의 내용은 다음과 같습니다.


Error Domain = NSURLErrorDomain Code = -1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection"UserInfo = {_ WKRecoveryAttempterErrorKey =, NSErrorFailingURLStringKey = http : //www.hoge.jp/, NSErrorFailingURLKey = http : //www.hoge.jp/, NSLocalizedDescription = The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}



이와 같이 에러가 나므로 ATS를 사용하지 않으려면 info.plist에 예외설정을 해야합니다.



Info.plist에 예외 설정하는 방법



Info.plist용 키를 확인하면 다음과 같습니다.



NSAppTransportSecurity (Dictionary)

  • NSExceptionDomains (Dictionary)
    • NSAllowsArbitraryLoads (Bool)
    • <domain-name-for-exception-as-string> (Dictionary)
      • NSExceptionMinimumTLSVersion (String)
      • NSExceptionRequiresForwardSecrecy (Bool)
      • NSExceptionAllowsInsecureHTTPLoads (Bool)
      • NSRequiresCertificateTransparency (Bool)
      • NSIncludesSubdomains (Bool)
      • NSThirdPartyExceptionMinimumTLSVersion (String)
      • NSThirdPartyExceptionRequiresForwardSecrecy (Bool)
      • NSThirdPartyExceptionAllowsInsecureHTTPLoads (Bool)


2개의 방법이 있는데, 

전체의 HTTP를 허용하는 방법과 도메인마다 설정해서 허용하는 방법이 있습니다.


1. 전체의 HTTP를 허용하는 방법(비추천이라고 합니다)

<key> NSAppTransportSecurity </ key> <dict> <key> NSAllowsArbitraryLoads </ key> <true /> </ dict>





2. ATS를 제외시킬 도메인을 Info.plist에 기재하는 방법


<key> NSAppTransportSecurity </ key> <dict> <key> NSExceptionDomains </ key> <dict> <key> www.xxx.com </ key> <dict> <key> NSTemporaryExceptionAllowsInsecureHTTPLoads </ key> <true /> </ dict> </ dict> </ dict>





몇가지 더 설정을 알아 보겠습니다.


  • NSExceptionMinimumTLSVersion: TLS 최소 버전을 문자열로 입력합니다. 아래 값들 중 하나를 넣을 수 있거나 생략할 수 있습니다.
    • TLSv1.0
    • TLSv1.1
    • TLSv1.2 (생략할 경우의 기본값)
  • NSExceptionRequiresForwardSecrecy: forward secrecy 라는 비밀키 암호화와 관련된 설정입니다.
  • NSExceptionAllowsInsecureHTTPLoads: HTTPS(SSL) 연결이 아니더라도 통신을 허용할 것인가를 YES 혹은 NO로 설정 할 수 있습니다.
  • NSIncludesSubdomains: 이 사이트의 하부도메인들에도 이 설정을 적용할 것인가를 YES 혹은 NO로 설정 할 수 있습니다.
  • NSThirdPartyExceptionMinimumTLSVersion: 써드파티 TLS 버전을 입력 할 수 있습니다.
  • NSThirdPartyExceptionRequiresForwardSecrecy: 역시 써드파티 Forward Secrecy 설정할 수 있습니다.
  • NSThirdPartyExceptionAllowsInsecureHTTPLoads: 역시나 써드파티 HTTPS 연결 강제를 설정합니다.




아래는 다음과 관련된 애플의 링크입니다.

https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html





WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  1 , 댓글  2개가 달렸습니다.
  1. 안녕하세요- ^^ 글 잘 보았습니다.
    제가 필요한 내용인것 같은데 혹시 퍼가도 될런지요?
  2. spicytomato 2018.02.07 11:36
    감사합니다. 유익했습니다. ㅎㅎ
secret





어플리케이션의 설정창을 코드로 띄우는 방법


카메라가 꼭 필요한 앱인데, 카메라 허용을 하지 않았을 경우, 

Alert창으로 "일반>설정에서 카메라를 허용해주세요" 보단,

아래의 코드를 이용해 직접 설정창으로 이동하게 해주면 훨씬 편리하게 기능을 수락하도록 유도하기 편할것 같다.


iOS8부터 사용이 가능하다.


if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {

    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];

    [[UIApplication sharedApplication] openURL:url];

}



이 코드를 실행하면, 이 앱의 설정으로 바로 이동한다



WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret





제러닉이란

NSArray NSMutableArray 기본적으로 NSObject 상속받는 모든 값이 들어갈 있지만, 제너릭을 이용하면 타입을 명확하게 지정 있다.


//제러닉. Object 처리할 있지만, 미리 명시한 오브젝트 형식을 담기위한 설정

NSMutableArray<NSString*> *strings = [[NSMutableArray alloc] init];

// NSString Object 넣을수 있다.

[strings addObject:@"String"];


//순서대로 (key) 타입과 (object value) 타입이다. 경우라면 키는 문자열이 되고 값은 숫자(NSNumber) 된다.

NSMutableDictionary<NSString *, NSNumber *> *someDictionary = [[NSMutableDictionary alloc] init];

[someDictionary setObject:@1 forKey:@"one"];






WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret






3D터치를 하기위해 View에 3d터치 등록.

*.m viewDidLoad
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
    [self registerForPreviewingWithDelegate:self sourceView:self.image1];
    [self registerForPreviewingWithDelegate:self sourceView:self.image2];
}
else {
    NSLog(@"ForceTouch not available. use LongPress...");
}

Head파일에 Delegate 등록.

@interface ViewController : UIViewController <UIViewControllerPreviewingDelegate>


픽을 했을때의 델리게이트. - (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location { DetailImageViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailImageView"]; vc.preferredContentSize = CGSizeMake(0, 300); vc._delegate = self; if(previewingContext.sourceView.tag == 1) { vc.imageTag = 1; }else if(previewingContext.sourceView.tag == 2) { vc.imageTag = 2; } previewingContext.sourceRect = CGRectMake(0, 0, previewingContext.sourceView.frame.size.width, previewingContext.sourceView.frame.size.height); return vc; }

- (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit
{
    [self presentViewController:viewControllerToCommit animated:YES completion:nil];
}


포스터치를 했을때, 보이는 메뉴 설정.

DetailImageViewController.m

//add previewActionItem
- (NSArray <id <UIPreviewActionItem>> *)previewActionItems
{
    UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"Soccer" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    [self._delegate callParentControll:TRUE];
    }];
    UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"BasketBall" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    [self._delegate callParentControll:FALSE];
    }];
    UIPreviewAction *action3_1 = [UIPreviewAction actionWithTitle:@"BaseBall" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    [self._delegate callParentControll:FALSE];
    }];
    UIPreviewAction *action3_2 = [UIPreviewAction actionWithTitle:@"VolleyBall" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
    [self._delegate callParentControll:FALSE];
    }];
    UIPreviewActionGroup *action3 = [UIPreviewActionGroup actionGroupWithTitle:@"More" style:UIPreviewActionStyleDestructive actions:@[action3_1, action3_2]];

    return @[action1, action2, action3];
}




ForceTouch 강도 체크.

Source

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *t = touches.anyObject;

    NSLog(@"touchesBegan : %f / %f", t.force, t.maximumPossibleForce);
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *t = touches.anyObject;

    NSLog(@"touchesMoved : %f / %f", t.force, t.maximumPossibleForce);
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *t = touches.anyObject;

    NSLog(@"touchesEnded : %f / %f", t.force, t.maximumPossibleForce);
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *t = touches.anyObject;

    NSLog(@"touchesCancelled : %f / %f", t.force, t.maximumPossibleForce);
}



WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret







이번 iOS9에서 제공되는 3d touch를 이용해 QuickAction(퀵액션)을 추가하겠습니다.


위의 화면과 같이 iOS9에서 제공되는 ShotCutItem 추가하는 방법을 알아 보겠습니다.


1. info.plist

<key>UIApplicationShortcutItems</key>
<array>
<dict>
<key>UIApplicationShortcutItemTitle</key>
<string>Action1</string>
<key>UIApplicationShortcutItemType</key>
<string>com.action1</string>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeConfirmation</string>
</dict>
</array>


2. code

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //shortcutitem add
    UIApplicationShortcutItem *item1 = [[UIApplicationShortcutItem alloc] initWithType:@"com.action2" localizedTitle:@"Action2" localizedSubtitle:@"title2" icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeLove] userInfo:nil];
    UIApplicationShortcutItem *item2 = [[UIApplicationShortcutItem alloc] initWithType:@"com.action3" localizedTitle:@"Action3" localizedSubtitle:@"title3" icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeCloud] userInfo:nil];
    UIApplicationShortcutItem *item3 = [[UIApplicationShortcutItem alloc] initWithType:@"com.action4" localizedTitle:@"Action4" localizedSubtitle:nil icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeTask] userInfo:nil];

    [[UIApplication sharedApplication] setShortcutItems: @[ item1, item2, item3 ]];

    return YES;
}



아이템을 클릭해 실행하면 다음의 코드의 델리게이트가 실행됩니다.

AppDelegate.m

//shortcutitem start delegate
- (void)application:(UIApplication *)application performActionForShortcutItem:(nonnull UIApplicationShortcutItem *)shortcutItem completionHandler:(nonnull void (^)(BOOL))completionHandler {
    NSLog(@"%@", [shortcutItem description]);
}




참고 : https://github.com/minjoongkim/iOS-3D-Touch-Example


WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret





download : http://github.com/minjoongkim/CustomKeyboard


1. import CustomKeyboard.h


2. 

*.h

@property (nonatomic, strong) CustomKeyBoard *keyboard;
@property (nonatomic, strong) IBOutlet UITextField *tf_keyboard;


*.m

-(void)viewDidLoad {
    UIScreen *mainScreen = [UIScreen mainScreen];
    keyboard = [[CustomKeyBoard alloc] init:mainScreen.bounds.size.width height:mainScreen.bounds.size.width*0.7 textView:tf_keyboard];
    tf_keyboard.delegate = self;
}






WRITTEN BY
블로blow
iOS 개발자 생활이야기

트랙백  0 , 댓글  0개가 달렸습니다.
secret