iOS는 안드로이드와 달리 자동 SMS 전송은 불가능하고  수신받을 번호와 메세지 내용은 미리 작성하여 문자 앱 호출이 가능하다.


recipents에 전화번호를 쓰고 message에 메세지 내용을 쓰면 된다.

- (IBAction)sendSms:(id)sender {
    
    if(![MFMessageComposeViewController canSendText]) {
        UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"이 기기는 SMS 발송을 지원하지 않습니다." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [warningAlert show];
        return;
    }
    
    NSArray *recipents = @[@"010-1234-5678"];
    NSString *message = [NSString stringWithFormat:@"SMS 발송 테스트"];
    
    MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
    messageController.messageComposeDelegate = self;
    [messageController setRecipients:recipents];
    [messageController setBody:message];
    
    // Present message view controller on screen
    [self presentViewController:messageController animated:YES completion:nil];
}



iOS 10과 더불어 업데이트 된 Xcode 8.x 버전으로 기존 앱을 빌드 했을 때,

기존에 tableView Header를 커스텀 뷰로 개발하였을 때 section header view가 보이지 않는 문제가 생긴다.


해당 문제는 기존에는 디폴트 높이를 설정하지 않아도 알아서 높이가 들어 갔는데 이번 버전부터는 해당 기능이 삭제가 된듯(....)

앞으로 점점 스토리보드 위주로 갈 것이라고 생각했는데  이번 건은 오히려 스토리보드의 역할이 줄어든 듯한 느낌.. 다른 이유가 있겠지.

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 30;
}

위 코드를 추가 하여 높이를 지정해주면 다시 section header view가 나타난다!

이 사실을 알기 전까지는 커스텀 헤더 뷰 지원이 중단 된 줄 알았음 ㅠ_ㅠ


아래는 대략적인 헤더 뷰 생성 예제 코드

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    UIView *customTitleView = [ [UIView alloc] initWithFrame:CGRectMake(10, 0, 300, 44)];
    UILabel *titleLabel = [ [UILabel alloc] initWithFrame:CGRectMake(20, 0, 300, 44)];
    
    if(section == 0)
    {
        titleLabel.text = @"섹션1";
        titleLabel.textColor = UIColorFromHEX(0x008080);
        
    }
    else if(section == 1)
    {
        titleLabel.text = @"섹션2";
        titleLabel.textColor = UIColorFromHEX(0xFFCC66);
        
    }
    else
    {
        titleLabel.text = @"섹션3";
        titleLabel.textColor = UIColorFromHEX(0x1E90FF);
    }
     titleLabel.font = [UIFont fontWithName:@"NanumBarunGothic" size:16];
    titleLabel.backgroundColor = [UIColor clearColor];
    [customTitleView addSubview:titleLabel];
    
     return customTitleView;
}


TableView 를 사용하다 보면, 어떤 셀을 선택했느냐에 따라서 다음 화면에서의 동작이나 표시되는 컨텐츠가 다를 수 있다.


먼저, Destination ViewController의 헤더파일에서 데이터를 받을 프로퍼티를 선언

@property (strong, nonatomic) NSString *receiveID;


스토리보드에서 Segue 연결을 셀에다 다음화면을 직접 연결하는게 아니라 뷰 컨트롤러에서 다음 뷰 컨트롤러를 연결한 후 Segue ID를 설정 해 준다

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    transData = [self.datalist objectAtIndex:indexPath.row]; // Cell의 index에 따라서 datalist 배열의 해당하는 index의 데이터를 transData 변수에 저장
    [self performSegueWithIdentifier:@"DataTestSegue" sender:self];  // DataTestSegue : 스토리보드에서 연결 후 설정 한 Segue ID
}


이 후에 화면전환 시 호출되는 prepareForSegue 메소드에서 데이터 넘기기

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    
    if ([[segue identifier] isEqualToString:@"DataTestSegue"]) {    // Segue ID에 따라서 구분. 한 화면에서 여러 뷰 컨트롤러로 나뉠 때 분기별로 나눠주면 됨
        SafeZoneDetailViewController *destination = [segue destinationViewController];
        destination.receiveID = transData;
        NSLog(@"ID: %@",transData);
    }
    else if ([[segue identifier] isEqualToString:@"newDataSegue"]) {
        NewSafezoneViewController *destination = [segue destinationViewController];
        
        destination.childID = receiveID;
        NSLog(@"child ID: %@",receiveID);
    }
}


타이머 선언

타이머 ID를 여러 개 선언해서 동시에 여러 개의 타이머를 돌리는 것도 가능하다.

NSTimer *timer;
NSString *str = @"Timer";
timer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self
                                           selector:@selector(updateMethod:) userInfo:str repeats:YES];


콜백 함수

- (void) updateMethod:(NSTimer *)incomingTimer
{
    NSLog(@"Inside update method");
    if ([incomingTimer userInfo] != nil)
        NSLog(@"userInfo: %@", [incomingTimer userInfo]);
}


아래와 같이 다중 타이머 선언 시 selector를 이용하여 각 타이머 별로 다중 작업이 가능하다

    NSTimer *locktimer;
    NSTimer *policytimer;
    NSString *lockstr = @"DeviceLock Timer";
    NSString *policystr = @"Policy Timer";
    
    locktimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self
                                           selector:@selector(updateDeviceLockInfo:) userInfo:lockstr repeats:YES];
    
    policytimer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self
                                           selector:@selector(updatePolicyInfo:) userInfo:policystr repeats:YES];
- (void) updatePolicyInfo:(NSTimer *)incomingTimer
{
    NSLog(@"Inside updatePolicyInfo method");
    if ([incomingTimer userInfo] != nil)
        NSLog(@"userInfo: %@", [incomingTimer userInfo]);
}
- (void) updateDeviceLockInfo:(NSTimer *)incomingTimer
{
    NSLog(@"Inside updateDeviceLockInfo method");
    if ([incomingTimer userInfo] != nil)
        NSLog(@"userInfo: %@", [incomingTimer userInfo]);
}




MySQL 접속

mysql -uroot -p mysql


접속 계정 생성

insert into user (host, user, password) values('%', 'User ID', password('User Password'));
flush privileges;


접근권한 설정(localhost)

DataBase Name.* to User ID@localhost identified by 'User Password' with grant option;


접근권한 설정(remote)

grant all privileges on DataBase Name.* to User ID@'%' identified by 'User Password' with grant option;
flush privileges;



사용자 등록 확인

select host, user, password from user;


my.cnf 파일 수정(bind-address 주석 처리)

#bind-address           = 127.0.0.1


MySQL 재시작

service mysql restart


root@ios-mdm:/etc/init.d# vi mdm_server.sh

root@ios-mdm:/etc/init.d# chmod +x /etc/init.d/mdm_server.sh

root@ios-mdm:/etc/init.d# update-rc.d mdm_server.sh defaults

iOS MDM 시스템 구축 후 가장 일반적으로 쓰이는 기능들은 DeviceInformationDevice Lock 일텐데,

DeviceInfo 관련해서는 지원이 중단되었던 장치 고유의 값들을 가져 올 수 있기 때문에 쓰는 것이 대부분이고..

DeviceLock 기능을 구현하기 위해 MDM 서버를 구축하고자 하는 개발자 분들을 위해 정리 해 드립니다.


MDM Reference Document를 보면 장치 잠금에 관련된 커맨드가 3가지가 있는데 각각의 차이점들을 정리하고자 합니다.


1. Activation Lock

활성화 잠금은 아이클라우드에서 내 아이폰 찾기 기능과 궁극적으로 같은 기능을 합니다.

MDM 커맨드는 일반적으로 Push Token을 통해 APNS를 통해 디바이스에 직접 커맨드를 전송하는데,

활성화 잠금은 Device Serial을 POST로 애플의 특정 url에 json타입으로 넘겨 주면 애플에서 장치로 커맨드를 전송하는 구성인 것 같습니다.


문제는, 활성화 잠금을 위해서는 Apple DEP(Device Enrollment Program)을 통해 장치 정보가 애플쪽에 등록이 된 기기에만 커맨드를 전송 할 수 있습니다.

DEP는 어떤 문제인지는 모르겠지만 아직 한국에서는 지원하지 않기 때문에 불가능하다고 판단되었습니다.


추가적으로 Device가 Supervise된 상태여야 합니다. 장치 감독 모드는 Apple Configurator를 통해 청사진을 적용하면 장치 리셋 후 감독모드로 설정됩니다.

Device Supervise를 무선으로 하는 방법은 없고 DEP를 통하거나 맥에 설치된 Apple Configurator에 USB로 한번 물리적으로 연결 해야만 합니다.


게다가 이전과는 다르게 장치 비밀번호를 알면 폰에서 바로 잠금 해제가 가능하도록 바뀌었기 때문에.... 의미가 없습니다.


2. EnableLostmode

테스트 결과 가장 강력한 기능의 잠금모드인데, MDM 커맨드를 받으면 해당 폰은 사용자가 어떠한 액션도 취할 수 없는 상태가 됩니다.

Activation Lock에서 비밀번호 입력 기능이 빠진 것이라고 보면 됩니다.

잠금 해제는 DisableLostmode 커맨드 수신을 통해서만 해제가 가능합니다.


헌데.. 가장 강력한 잠금모드이지만...! iOS 9.3버전 이상이 설치된 장치에만 적용되며  역시나....Device Supervise가 필요합니다.

한마디로 수작업으로 맥에 연결해서 초기화를 한번 거쳐야 한다는 것입니다..


3. DeviceLock

별 것 없습니다.  DeviceLock 커맨드를 수신한 장치는 어떠한 동작 중이든 잠금화면으로 돌아갑니다. (장치가 슬립상태일 때 전원 버튼 한번 눌렀을 때의 그 화면...)


Apple MDM은 보면 볼수록 컨셉이 확실한 것 같네요.

Apple Configurator의 역할은 기관이나 기업에서 대량 구매한 iOS 장치들을 초기 설정을 동일하게 하기 위함과 감독 권한을 얻기 위한 것이라고 보여집니다.

Apple Configurator의 청사진에는 특정 앱이나 프로파일등을 미리 넣어 놓을 수 있어 특정 와이파이 AP만 연결되게 한다던가, 사용 할 수 있는 앱을 특정한다던가

다수 장치 관리의 편의성에 초점을 두고 있다는 느낌이 듭니다.


구현하려던 잠금 기능은 2. EnableLostmode 였는데, 무선으로 장치 감독권한을 얻을 수 있는 방법을 알아봤으나,

애플에서 온 답장에 "Our engineers have reviewed your request and have concluded that there is no supported way한 문장에  멘붕...


이상 이번에 시스템을 구축을 하면서 얻은 경험을 공유드립니다.

MDM솔루션 제공 업체를 통하지 않고 자체 개발 하시려는 분들에게 도움이 되었으면 좋겠습니다^^..     


아이 빡쳐 ^^






맥 OS X에 node.js 및 폰갭 설치 방법


1. Xcode 설치 (따로 말 안해도 개발자라면 설치되어 있을거라고 믿습니다...!)

2. node.js 설치 (https://nodejs.org/en/)

3. node.js를 통해 cordova 설치

터미널에서 아래 명령어를 통해 cordova 설치

sudo npm install -g cordova


맥의 로그인 비밀번호를 입력한 후 설치가 완료되면 아래와 같이 정보가 표시됨

/usr/local/bin/cordova -> /usr/local/lib/node_modules/cordova/bin/cordova

cordova@6.1.1 /usr/local/lib/node_modules/cordova

├── underscore@1.7.0

├── q@1.0.1

├── ansi@0.3.1

├── nopt@3.0.1 (abbrev@1.0.7)

├── cordova-common@1.2.0 (cordova-registry-mapper@1.1.15, unorm@1.4.1, underscore@1.8.3, q@1.4.1, semver@5.1.0, shelljs@0.5.3, osenv@0.1.3, elementtree@0.1.6, bplist-parser@0.1.1, glob@5.0.15, plist@1.2.0)

├── update-notifier@0.5.0 (is-npm@1.0.0, semver-diff@2.1.0, string-length@1.0.1, chalk@1.1.3, repeating@1.1.3, configstore@1.4.0, latest-version@1.0.1)

└── cordova-lib@6.1.1 (valid-identifier@0.0.1, opener@1.4.1, properties-parser@0.2.3, semver@4.3.6, shelljs@0.3.0, nopt@3.0.6, dep-graph@1.1.0, cordova-app-hello-world@3.10.0, xcode@0.8.0, aliasify@1.9.0, tar@1.0.2, init-package-json@1.9.3, cordova-serve@1.0.0, request@2.47.0, npm@2.15.4, cordova-js@4.1.4)


설치 완료 후 cordova 버전 확인

cordova -v

1. mysql-server, mysql-client 설치

설치 중에 root 비밀번호를 두번 묻는데, 사용할 비밀번호 입력

$ sudo apt-get install mysql-server mysql-client


2. 설치 후 /etc/mysql/mysql.conf.d/mysqld.conf 파일 수정

$ sudo vi /etc/mysql.conf.d/mysqld.conf
......
# bind-address = 127.0.0.1
......


3. mysql 서비스 재시작

 $ sudo service mysql restart


4. 테스트 DB 생성 및 유저 생성/접근 권한 설정

$ mysql -u root -h localhost -p
mysql> CREATE DATABASE pidb;
mysql> CREATE USER 'pi'@'localhost' IDENTIFIED BY '비밀번호 설정';
GRANT ALL PRIVILEGES ON pidb.* TO 'pi'@'localhost';
FLUSH PRIVILEGES;
$ mysql -u pi -h localhost pidb -p

패스워드 입력 후 접속 된다면 설정 완료


5. 파이썬 연동 되었는지 확인 위해 파이썬 쉘에서 mysql 모듈 import

$ python
>>> import MySQLdb

ImportError: No module named MySQLdb


6. 연동되지 않았다면, http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/ 에서

압축 파일을 다운로드 받아서 압축 해제


7. 필수 패키지 설치 후 setup.py 빌드/설치

sudo apt-get install libmysqlclient-dev
$ python setup.py build
$ sudo python setup.py install


8. 파이썬 쉘 실행 후 mysql 모듈 import 하여 연동 확인

$ python
>>> import MySQLdb


파이썬에서 문자열은 ""를 이용하여 정의할 수 있다.

s = "Hello World"
print(s)


Accesing array elements

대괄호를 이용하여 문자열의 요소에 엑세스

print(s[0])


String Slicing

파이썬에서는 문자열을 범위를 지정하여 Substring 할 수 있다.

문자열을 자를 인덱스 범위를 3가지 방법으로 지정 할 수 있다.

>>> s = "Hello World"
>>> s[:3]
'Hel'
>>> s[3:]
'lo World'
>>> s[1:3]
'el'
>>> slice = s[0:5]
'hello'


'프로그래밍 > Python' 카테고리의 다른 글

Python Tutorial : Comments [15]  (0) 2016.04.16
Python Tutorial : Date and Time [14]  (0) 2016.04.16
Python Tutorial : Modules [13]  (0) 2016.04.16
Python Tutorial : Line charts [12]  (0) 2016.04.16
Python Tutorial : Statistics [11]  (0) 2016.04.15

+ Recent posts