Swfit의 compare를 이용하면 Date끼리의 비교도 가능하다.

이글에서 할 순서는

1. 동일 문자열 형식으로 비교할 값을 정의한다.

2. 동일한 DateFormatter로 String 을 Date 형으로 변경한다

3. 비교

비교할 형식은이다 "yyyy-MM-dd "

아래는 gist로 작성한 예제 코드이다.

 

compare는 Same, DESC, ASC로 세가지의 결과를 반환해주는데

이를 이용해 Date 끼리의 비교를 따로 함수로 정의해서 사용하거나 바로 이렇게 사용할수 있다.

https://developer.apple.com/documentation/uikit/uiview/1622541-bringsubviewtofront

 

Apple Developer Documentation

 

developer.apple.com

 

쓰다보면 항상 헷갈려서 검색하게되어 글을 남겨본다

swift로 앱을 만들다보면 하나의 부모뷰에 여러 자식들이 들어가게 되는데

겹쳐있는 자식들중 하나를 맨앞으로 끌어오고싶을때 쓰는 메소드이다.

쓰는 방법은

부모뷰.bringSubviewToFront(맨앞으로 당길 자식뷰)

ex: https://www.hackingwithswift.com/example-code/uikit/how-to-bring-a-subview-to-the-front-of-a-uiview )

parentView.bringSubviewToFront(childView)

다음엔 잊지않기를 바란다

Showing All Messages In  ~~~~~~~/WebP.framework/WebP(anim_decode.o), building for iOS Simulator, but linking in object file built for iOS, file ~~~~~~~~/WebP.framework/WebP for architecture arm64

라고 뜨며 앱빌드가 되지 않는다.

재택근무하며 집에 있는 맥북은 intel 기반이고 사무실은 m1이라 왔다갔다 하면서 사용되는 라이브러리가 달라 발생하는 문제로 보인다.

문구 그대로 검색하면 나오는 방법은

프로젝트 빌드 설정의 Excluded Architectures에 arm64를 넣으면 만사ok 라는데

이를 행하면 아래와 같은 오류가 발생한다.

~~/ Could not find module 'SnapKit' for target 'arm64-apple-ios-simulator'; found: x86_64-apple-ios-simulator, at: ~~~/Library/Developer/Xcode/DerivedData/Store-cnoxkdqeqkddojftbwfdqjacdaxo/Build/Products/Debug-iphonesimulator/SnapKit/SnapKit.framework/Modules/SnapKit.swiftmodule

오랜 세월을 함께한 나의 전우같은 스냅킷님에서 문제가 발생했다.

대충 보면 arm64시뮬레이터로 지정됐는데 해당 모듈을 찾을수 없다고한다.

/**

* 이쯤 되면 진짜 WebP.framework를 사용하고 싶지 않다.

* 더불어 YY이름이 여럿 붙은 오픈소스가 이용되는데 이게 다 무슨소린가 싶다

* 왓츠앱의 스티커 깃헙에서 제공해주고있는 오픈소슨데 왓츠앱이면 꽤 큰곳인데 이런 따로 오픈소스를 사용한다니 이해가 되지 않는다.

* 왓츠앱 스티커 관련 작업이라 공식에서 제공해주는걸 해야겠지싶기도 하다.

**/

여러가지를 시도해본 결과 아래와같은 절차를 행하면 동작한다.

우선

1. /Users/유저폴더/Library/Developer/Xcode/DerivedData  폴더를 비운다.

2. 프로젝트 루트폴더의 Podfile.lock 를 삭제

3. 동일 폴더의 Pod폴더를 삭제하여 설치된 파일을 삭제한다.

4. pod install --repo-update 를 실행

5. 워크스페이스파일로 프로젝트를 연다

6. 클린을 해준다 ( 쉬프트 + 커맨드 + k )

7. 프로젝트 -> 빌드 -> Excluded Architectures 에 Any iOS Simulator SDK 의 arm64 를 넣어서 빌드에서 제외해준다.

https://jusung.github.io/Xcode12-Build-Error/ 의 "1. EXCLUDED_ARCHS에 arm64를 추가" 에 해당한다.

8. Pod 프로젝트 설정에서 7번과 동일하게 Excluded Architectrues 에 arm64를 넣어준다.

9. 시뮬레이터에서 빌드 테스트.

10. 해피엔딩

 

나와 동일한 방법을 행했는데도 문제가 발생했다면 나도 모르겠다.

https://stackoverflow.com/questions/18946302/uinavigationcontroller-interactive-pop-gesture-not-working

UINavigationController에 붙어있는 ViewController이지만백제스쳐가 동작하지 않는 경우가 왕왕있다.

 

해당 문제는 제스쳐 이벤트 끼리의 충돌문제인것으로 보인다이때 네비게이션 제스쳐를 하나의 UINavigationController 내부의 viewControllers개수가 하나보다 많을때만 동작하도록 지정해주는 걸 달아주면 이상하게도 동작을 잘한다.

 

해당 소스는 상단 스택오버플로우의 답에 달린 코드이다.

class NavigationController: UINavigationController, UIGestureRecognizerDelegate {

    /// Custom back buttons disable the interactive pop animation
    /// To enable it back we set the recognizer to `self`
    override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }

}

Swift 에서는 접근레벨을 5개로 나뉜다

open

public

internal

fileprivate

private

 

open은 말그대로 open된 소스고 public은 오픈에서 오버라이드와 서브클래싱을 막는다

이렇게 점점 접근 가능한 범위를 줄이고

마지막에 와서는 private, 해당 내역이 선언된 구역에서만 사용가능하게 된다

 

swift에서는 변수의 get 과 set을 재정의를 해주는게 간편히 가능한데

이와 위의 접근레벨과 섞으면 내부에선 읽고 쓰고, 외부에서는 읽기만 가능한게 만들 수 있다.

단순히 둘이 섞어보자면 아래와같게 하여 값을 보호할 수 있다.

public class AccessLevels {
    private var realValue: String = ""
    public var value: String {
        get {
            return realValue
        }
    }
    
    func function() {
        realValue = "real value"
    }
}

let test: AccessLevels = .init()
print(test.value) // "\n"
test.function()
print(test.value) // real value

콘솔 출력값은 주석처리한 부분이다

다른 언어에서 get, set 함수를 따로 만들어 사용하는것과 유사하다

실제로 사용하는 변수는 realValue이고 외부에서 해당 값을 보기위해 realValue를 접근하려고하면 오류가 나고

이를 보기위해서는 value를 봐야한다.

 

사실 이를 더 간편히 만들 수 있는게 있는데 이게바로 이글의 주제인 private(set) 이다.

public class PrivateSet {
    private(set) var value: String = ""
    
    func function() {
        value = "real value"
    }
}
let test: PrivateSet = .init()
print(test.value) // "\n"
test.function()
print(test.value) // "real value"

PrivateSet 클래스는 위의 AccessLevels 클래스와 동일한 기능을 하는 클래스이다

get을 지정해주는 변수와 실제 값을 담아두는 변수가 private(set) 으로 value 하나로 통일되었다.

 

외부에서 값이 변경되지 않도록 지켜주는 기능또한 여전하다.

 

private(set) 간편!

들어온 데이터를 같은 날짜별로 분리해야할 일이 생겨 오랜만에 포스팅에 좋겠다 싶어서 남긴다.

스위프트의 Dictionary 는 무려 이러한 분할을 조건만 명시해주면 알아서 해준다.

이걸 어떻게 구현해야할지 막막해지며 포문안에 키값을 추출하며 분리해 나가야하나 싶었는데

그냥 알아서 제공해줘서 매우 기쁘다.

공식 문서는 아래와 같다.

https://developer.apple.com/documentation/swift/dictionary/3127163-init

 

Apple Developer Documentation

 

developer.apple.com

 

생성자 형태.

init<S>(grouping values: S, by keyForValue: (S.Element) throws -> Key) 
rethrows where Value == [S.Element], S : Sequence

 

 

형태는 S를 줘서 키값을 리턴해주면된다.

바로 코드로 테스트해보면

데이터 형태는 TestItem, 테스트 하기 편하게 id와 updateAt 만 주고

struct TestItem: Codable {
    let id: Int
    let updatedAt: String?
}

 

받은 데이터는 아래와같은거로 가정 16,17은 업데이트 값이 이상하게 들어간 경우를 테스트

let testArray: [TestItem] = [
    .init(id: 0, updatedAt: "2022-04-22T13:18:08.000Z"),
    .init(id: 1, updatedAt: "2022-04-22T13:18:08.000Z"),
    .init(id: 2, updatedAt: "2022-04-08T13:18:08.000Z"),
    .init(id: 3, updatedAt: "2022-04-07T13:18:08.000Z"),
    .init(id: 4, updatedAt: "2022-04-07T13:18:08.000Z"),
    .init(id: 5, updatedAt: "2022-04-05T13:18:08.000Z"),
    .init(id: 6, updatedAt: "2022-03-20T13:18:08.000Z"),
    .init(id: 7, updatedAt: "2022-03-17T13:18:08.000Z"),
    .init(id: 8, updatedAt: "2022-03-17T13:18:08.000Z"),
    .init(id: 9, updatedAt: "2022-03-16T13:18:08.000Z"),
    .init(id: 10, updatedAt: "2022-02-08T13:18:08.000Z"),
    .init(id: 11, updatedAt: "2022-01-08T13:18:08.000Z"),
    .init(id: 12, updatedAt: "2021-01-08T13:18:08.000Z"),
    .init(id: 13, updatedAt: "2021-04-08T13:18:08.000Z"),
    .init(id: 14, updatedAt: "2021-04-30T13:18:08.000Z"),
    .init(id: 15, updatedAt: "2020-04-01T13:18:08.000Z"),
    .init(id: 16, updatedAt: "2020-04-0113:18:08.000Z"),
    .init(id: 17, updatedAt: nil),
]

 

이제 그룹핑하고 찍어보자면

updatedAt이 제대로 입력되지 않은 녀석은 blank라는 키로 따로 뺏다.

여기선 검사를 안했지만 정규식패턴으로 updatedAt을 검사하는 과정을 넣는게 좋을듯 보임

let groupedDict: [String: [TestItem]] = Dictionary(grouping: testArray)
{ item in
    guard let updatedAt: String = item.updatedAt else { return "blank"}
    let split = updatedAt.split(separator: "T")
    guard split.count == 2 else { return "blank" }
    let dateString: String = String(split[0])
    return dateString
}

for key in groupedDict.keys {
    print("key : \(key)")
    let values: [TestItem] = groupedDict[key]!
    for value in values {
        print("id: \(value.id), updatedAt: \(value.updatedAt ?? "")")
    }
    print("")
    print("")
}

 

출력:

key : 2021-01-08
id: 12, updatedAt: 2021-01-08T13:18:08.000Z


key : 2022-04-07
id: 3, updatedAt: 2022-04-07T13:18:08.000Z
id: 4, updatedAt: 2022-04-07T13:18:08.000Z


key : 2020-04-01
id: 15, updatedAt: 2020-04-01T13:18:08.000Z


key : blank
id: 16, updatedAt: 2020-04-0113:18:08.000Z
id: 17, updatedAt: 


key : 2022-03-20
id: 6, updatedAt: 2022-03-20T13:18:08.000Z


key : 2022-02-08
id: 10, updatedAt: 2022-02-08T13:18:08.000Z


key : 2022-01-08
id: 11, updatedAt: 2022-01-08T13:18:08.000Z


key : 2022-03-17
id: 7, updatedAt: 2022-03-17T13:18:08.000Z
id: 8, updatedAt: 2022-03-17T13:18:08.000Z


key : 2022-04-08
id: 2, updatedAt: 2022-04-08T13:18:08.000Z


key : 2022-03-16
id: 9, updatedAt: 2022-03-16T13:18:08.000Z


key : 2022-04-05
id: 5, updatedAt: 2022-04-05T13:18:08.000Z


key : 2022-04-22
id: 0, updatedAt: 2022-04-22T13:18:08.000Z
id: 1, updatedAt: 2022-04-22T13:18:08.000Z


key : 2021-04-30
id: 14, updatedAt: 2021-04-30T13:18:08.000Z


key : 2021-04-08
id: 13, updatedAt: 2021-04-08T13:18:08.000Z

딕셔너리는 순서를 지키지 않기에 

만약 데이터로 가공해야한다면 키값에 대한 정렬이 필요해보인다.

scrollView를 받고있는 collectionView는 베이스와 같이 스크롤을 프로그래밍으로 움직이는게 가능하다

이를 간편하게 만들어둔게 scrollToItem( at,at, animated )

간편해서 

indexPath만 따로 만들어두면 곧장 해당 아이템이 있는곳으로 날아갈수있다.

 

iOS 15 시뮬레이터에서 돌려보던중 해당 기능이 동작하지 않는걸 발견했는데 도저히 이유를 모르겠던중 아래와같은 답변을 찾았다

https://developer.apple.com/forums/thread/663156

 

UICollectionView scrollToItem brok… | Apple Developer Forums

Hi All Where we have to put this code? self.collectionView.isPagingEnabled = false self.collectionView.scrollToItem(at: indexPath, at: .left, animated: false) self.collectionView.isPagingEnabled = true In my scenario, Let's suppose there are 5 items to sho

developer.apple.com

 

iOS14에서 나온 문제건만..

iOS15에서도 고쳐지지 않았다

 

문제가된 경우는

scolltoITem하기전 isPgingEnabled가 true로 설정해둘경우이다

그래서 

아래와같이 하면된다.

item번호 11번으로 가는 코드이다.

collectionView.isPagingEnabled = false
collectionView.scrollToItem(at: IndexPath(item: 11, section: 0), at: .centeredHorizontally, animated: true)
collectionView.isPagingEnabled = true

scrollToITem내부 파라미터에대해서는 공식 홈페이지를 보시고...

해당 함수를 호출하기전 paging을 꺼둔다.

그리고 다시킨다

 

어처구니없는 해결방법이지만 동작을 잘해서 그냥 해당방법으로 가기로 결정했다.

 

+ Recent posts